home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume26 / xc-4.1 / part03 < prev    next >
Encoding:
Text File  |  1993-04-13  |  92.7 KB  |  3,428 lines

  1. Newsgroups: comp.sources.unix
  2. From: jpr@jpr.com (Jean-Pierre Radley)
  3. Subject: v26i152: xc-4.1 - a serial communications program, V4.1, Part03/03
  4. Sender: unix-sources-moderator@vix.com
  5. Approved: paul@vix.com
  6.  
  7. Submitted-By: jpr@jpr.com (Jean-Pierre Radley)
  8. Posting-Number: Volume 26, Issue 152
  9. Archive-Name: xc-4.1/part03
  10.  
  11. #! /bin/sh
  12. # This is a shell archive.  Remove anything before this line, then unpack
  13. # it by saving it into a file and typing "sh file".  To overwrite existing
  14. # files, type "sh file -c".  You can also feed this as standard input via
  15. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  16. # will see the following message at the end:
  17. #        "End of archive 3 (of 3)."
  18. # Contents:  xc.nro xcscrpt.c
  19. # Wrapped by vixie@gw.home.vix.com on Wed Apr 14 00:22:47 1993
  20. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  21. if test -f 'xc.nro' -a "${1}" != "-c" ; then 
  22.   echo shar: Will not clobber existing file \"'xc.nro'\"
  23. else
  24. echo shar: Extracting \"'xc.nro'\" \(45748 characters\)
  25. sed "s/^X//" >'xc.nro' <<'END_OF_FILE'
  26. X.ds ]V version 4.1 JPRadley 10 April 1993
  27. X.tm
  28. X.tm xc.nro \*(]V
  29. X.tm
  30. X.tm If this isn't preprocessed by 'tbl' and postprocessed by 'col',
  31. X.tm well, my dear, really, why waste your time and mine?
  32. X.tm
  33. X.ta 4 4 4 4 4 4 4 4
  34. X.po 0
  35. X.nrLL 7.9i
  36. X.ll\n(LLu
  37. X.lt\n(LLu
  38. X.TH XC L
  39. X.SH Name
  40. xc \- communications program 
  41. X.SH Syntax
  42. X.B xc
  43. X.RB [ -l device]
  44. X.RB [ -s script\ |
  45. X.BR -t ]
  46. X.SH Description
  47. X.B Xc
  48. calls out over a serial port to another computer. It can manage an interactive
  49. session or be called from
  50. X.BR cron (C).
  51. It has various means for transferring files between computers, and can be
  52. partially or totally under the control of scripts.
  53. X
  54. X.B Xc
  55. starts up by reading the file 
  56. X.IR .xc ,
  57. which is sought for in this order: in "XC_PATH", in the current directory,
  58. in your
  59. X.I HOME
  60. directory, or in a default directory defined at compile time. This startup
  61. file may contain any of the valid
  62. X.I SET
  63. commands described below. 
  64. X.B Xc
  65. then displays the current settings, and presents an
  66. X.I <XC>
  67. prompt, unless either the \fB-t\fR or \fB-s\fR option was present.
  68. X.SS \fIOptions\fR
  69. X.TP 9
  70. X.BI -l device
  71. Use the specified 
  72. X.IR device ,
  73. which need not be the full pathname, e.g. "/dev/tty1A" or "tty1A" are
  74. equally acceptable.
  75. The line could either be directly connected to another computer, or have a
  76. modem on it. In either case (if a value was defined for DIDO at compile time),
  77. X.B xc
  78. attempts to suspend a
  79. X.BR getty (M)
  80. process that might be on that line, and then use the
  81. X.BR uucp (C)
  82. X.I LCK..file
  83. mechanism to lock the line.
  84. X.TP
  85. X.BI -s script
  86. XExecute the specified
  87. X.I script
  88. after program startup.
  89. X.TP
  90. X.B -t
  91. Go directly to terminal mode. The default is to go to command mode on program
  92. startup. This option is incompatible with the 
  93. X.B -s
  94. option.
  95. X.SS \fIEnvironment\fR
  96. If the environment contains a variable called "MODEM", then the value
  97. of that variable will be the default device. As with the
  98. X.B -l
  99. flag, this need not be the
  100. full pathname. To set such a variable, in the Bourne shell, you might say:
  101. X.IP
  102. MODEM=tty01; export MODEM
  103. X.P
  104. or, in the C shell,
  105. X.IP
  106. setenv MODEM tty01
  107. X.P
  108. If the environment contains a variable called "BYEttyxx", where "ttyxx"
  109. stands for the basename of the modem line selected either by the MODEM
  110. variable or by the "-l" command option, then the value of that variable
  111. will be sent to the modem when the program terminates. This is useful
  112. for setting the modem back to some preselected state. Example:
  113. X.IP
  114. BYEtty01=ATZ ; export BYEtty01
  115. X.P
  116. Whatever string is contained in the "Byexxxxx" variable will be
  117. sent to the modem preceded and followed by a carriage-return.
  118. X
  119. Newer modems can be preset to revert to a predetermined state when the
  120. DTR signal of the computer is dropped, and so would not need to avail
  121. themselves of this feature. Furthermore,
  122. X.B getty
  123. programs which have been suspended, and which restart as
  124. X.B xc
  125. exits, will reset a modem using dialers or chat scripts referred to in the
  126. X/usr/lib/uucp/Devices file.
  127. X
  128. The environment may contain a variable called "XC_PATH", consisting
  129. of colon separated absolute paths to directories. If this variable is set to,
  130. say, "/usr/joe/xc:/usr/lib/xc", then those two directories are the
  131. first places searched for any scripts.
  132. X
  133. X.SS \fICommand Mode\fR
  134. When entering characters in command mode (that is, at the 
  135. X.I <XC>
  136. prompt), control characters echo as ^x, where "x" is the control character that
  137. was entered. Backspacing (using whatever key is defined in the current
  138. environment) backspaces over both positions of the displayed control character,
  139. as expected. 
  140. X
  141. An "interpreted" key can be added in a manner similar to that of
  142. X.BR vi (C);
  143. simply type ^V followed by the character to insert (backspace, delete,
  144. carriage return, or newline).
  145. X
  146. The following commands are available at the
  147. X.I <XC>
  148. command prompt:
  149. X
  150. X.TS
  151. tab(@);
  152. l1B lw(56) .
  153. c
  154. cis@T{
  155. Respond to an ENQ signal for a CIS B-Plus protocol transfer. This command is
  156. used for both uploading and downloading from CompuServe.
  157. T}
  158. X
  159. d
  160. dial@T{
  161. XEnter the dial directory. Returns to the \fI<XC>\fR
  162. prompt if exited without dialing, but returns to terminal mode after
  163. dialing or running a script.
  164. T}
  165. X
  166. s \fIfile
  167. script \fIfile\fR@T{
  168. XExecute \fIfile\fR, which contains appropriate \fBxc\fR
  169. script commands. Returns to terminal mode when the script is complete.
  170. T}
  171. X
  172. rb \fIfile\fR@(XMODEM binary receive)
  173. rt \fIfile\fR@T{
  174. X(XMODEM text receive) Receive \fIfile\fR from the remote system.
  175. T}
  176. X
  177. sb \fIfile\fR@(XMODEM binary send)
  178. st \fIfile\fR@T{
  179. X(XMODEM text send) Transmit \fIfile\fR to the remote system.
  180. T}
  181. X
  182. set \fR[\fIoptions\fR]@T{
  183. Display or set the transmission parameters used by \fBxc\fR. See below.
  184. T}
  185. X
  186. t
  187. term@Enter terminal mode.
  188. X
  189. x
  190. q
  191. exit
  192. quit@Exit program. Return to invoking program/shell.
  193. X
  194. h
  195. hangup@Hang-up the modem.
  196. X
  197. X%p \fIlocal\fR_\fIname\fR@T{
  198. X[\fIremote_name\fR] Transmit (put) a file to a remote Unix system. This command
  199. uses standard Unix utilities on the other end. \fIRemote_name\fR defaults to
  200. the same pathname as \fIlocal_name\fR if not otherwise specified.
  201. T}
  202. X
  203. X%t \fIremote\fR_\fIname\fR@T{
  204. X[\fIlocal_name\fR] Receive (take) a file from a remote Unix system. This
  205. command uses standard Unix utilities on the other end. \fILocal_name\fR
  206. defaults to the same pathname as \fIremote_name\fR if not otherwise specified.
  207. T}
  208. X
  209. X?
  210. help@T{
  211. Displays a brief summary of \fBxc\fR commands, the SET options, and
  212. terminal-escape commands.
  213. T}
  214. X.TE
  215. X.P
  216. X[Note: a compile-time option can disable the following three options to
  217. prevent any access to shells or other programs outside of
  218. X.BR xc .]
  219. X
  220. X.TS
  221. tab(@);
  222. l6B lw(56) .
  223. X! \fIcommand\fR@T{
  224. XExecute the specified \fIcommand\fR as a child process. If \fIcommand\fR is
  225. omitted, execute a local interactive shell.
  226. X(A space is required between the \fB!\fR and the \fIcommand\fR.)
  227. T}
  228. X
  229. X!!@Re-execute the last shell command string. 
  230. X
  231. X$ \fIcommand\fR@T{
  232. XExecute a shell \fIcommand\fR with stdin and stdout redi\%rected to the
  233. modem port. This effectively puts the computer into a "host" mode.
  234. X(A space is required between the \fB$\fR and the \fIcommand\fR.)
  235. T}
  236. X.TE
  237. X.SS \fIUsing the SET Command\fR
  238. The
  239. X.I SET
  240. command is used to display and set/reset
  241. X.BR xc 's
  242. tunable parameters. Any of these commands may be placed in the
  243. X.I .xc
  244. file which is read upon starting up
  245. X.BR xc .
  246. X
  247. Used alone,
  248. X.I SET
  249. displays
  250. X.BR xc 's
  251. current parameters.
  252. X
  253. The following parameters can be set (note that except in the case of
  254. X.IR name s,
  255. these commands are case-\fIin\fRsensitive, and that there alternative forms
  256. of the commands shown on the XC help screen):
  257. X
  258. X.TS
  259. tab(@) ;
  260. l1 lw(54) .
  261. set auto on|off@T{
  262. Sets the auto-capture feature. When entering terminal mode, capturing
  263. commences without the necessity to manually request it.
  264. T}
  265. X
  266. set bps \fIvalue\fR
  267. set baud \fIvalue\fR@T{
  268. Set the desired bits/second rate. Supported bps rates are 300, 1200, 2400,
  269. X9600 (and, if included at compile time, 19200 and 38400).
  270. T}
  271. X
  272. set cfile \fIname\fR@Set the \fIname\fR of the capture file.
  273. X
  274. set cis on|off@T{
  275. Set response to a CompuServe file transfer request (<ENQ>). An "on" value
  276. specifies that when in terminal mode, an <ENQ> character will launch a CIS
  277. B-Plus protocol transfer. This parameter should be set "off" when not
  278. connecting to CompuServe, as phone line noise may cause a bogus file transfer
  279. request.
  280. T}
  281. X
  282. set cr on|off@T{
  283. In uploads using B-Plus protocol, setting this option "on" will insert a
  284. carriage-return after each newline in an ASCII file. If you expect your ASCII
  285. upload to be captured by DOS users, it is best to set cr "on". If your ASCII
  286. upload is meant strictly for Unix users, then you may set cr "off".
  287. The B-Plus protocol implementation used by \fBxc\fR
  288. will always strip end-of-line carriage-returns from incoming ASCII files.
  289. T}
  290. X
  291. set hdplx on|off@T{
  292. Sets the half-duplex flag. If this flag is set, then all characters typed at
  293. the keyboard while in terminal mode will be echoed to the screen as well as
  294. sent to the modem line. Useful for remote systems that don't echo characters.
  295. T}
  296. X
  297. set menu on|off@T{
  298. By default, a one-line menu is displayed above the \fI<XC>\fR prompt to
  299. remind the user of options most frequently chosen at this point:
  300. X"[d]ial directory [t]erminal mode [q]uit [s]cript [?]help".
  301. Setting the option "off" turns off this display.
  302. T}
  303. X
  304. set nl on|off@T{
  305. If this option is set "on", then newlines are sent as
  306. carriage-returns. If this option is set "off", then newlines are sent as
  307. newlines (carriage-returns are in any case always sent as carriage-returns).
  308. This option applies to input from the keyboard, or from a disk file using the
  309. X"\fBXCAPE\fR \fBF\fR" facility or a script "type" command (See below).
  310. This option has no effect on built-in XMODEM or B-Plus protocol transmissions,
  311. and has no effect on incoming data.
  312. T}
  313. X
  314. set pfile \fIname\fR@Set the \fIname\fR of the phone directory.
  315. X
  316. set proto \fIvalue\fR@T{
  317. This refers to the serial port character protocol, not to a file transfer
  318. protocol. The possible values are 8N1, 7E2, or 7O2, (case insensitive) which
  319. respectively set the modem port to a character size of 8 with no parity, or a
  320. character size of 7 with either even or odd parity. During B-Plus or XMODEM
  321. transfers, the port protocol is set to 8N, and reverts to its prior setting
  322. thereafter.
  323. T}
  324. X
  325. set xcape \fIchar\fR
  326. set escape \fIchar\fR@T{
  327. Set the \fBXCAPE\fR character (see below) to \fIchar\fR. This should be set to
  328. some non-printing character (other than backspace, tab, newline, of course).
  329. The character may be entered by depressing the Control key first
  330. and then typing a letter, or by typing a carat (^) followed by a letter.
  331. T}
  332. X
  333. set xon on|off
  334. set xoff on|off@T{
  335. Set XON/XOFF flow control flag. If "on", the program will honor the XOFF
  336. control character and wait until an XON character is received before
  337. transmitting any more information. If "off", the program will ignore
  338. XXOFF/XON requests.
  339. T}
  340. X.TE
  341. X.SS \fITerminal Mode\fR
  342. In terminal mode, all characters typed at the keyboard are sent to the
  343. modem, except that newline characters (0x0A) are translated to
  344. carriage-returns (0x0D) when you have "set nl 'on'"; all characters received
  345. from the modem are displayed on the local terminal screen.
  346. X
  347. X\fBXCAPE\fR is a key that will, when typed in terminal mode, introduce an
  348. X.B xc
  349. X"escape" command. Don't confuse \fBXCAPE\fR with the <ESCAPE> key.
  350. The default definition for \fBXCAPE\fR is ASCII 1, or Control-A. It can 
  351. be redefined at run time with the "set escape \fIC\fR" command (or it can be
  352. redefined in the
  353. X.I .xc
  354. startup script).
  355. X
  356. When the \fBXCAPE\fR key is typed in terminal mode, the program will examine
  357. the next key pressed. If this key has been "bound" to perform a certain
  358. function, that function will be performed; otherwise, the second character is
  359. sent to the modem. Thus, to send the \fBXCAPE\fR character through the modem,
  360. it is necessary to press the key TWICE.
  361. X
  362. Thus the ESCAPE key itself would be a terrible choice for \fBXCAPE\fR, because
  363. it is used so often by other programs: if you were logged into a remote system
  364. and running, say, \fBvi\fR, it would be a great annoyance to have to hit ESCAPE
  365. twice to get it transmitted once.
  366. X
  367. The following keys (case \fIin\fRsensitive) are bound at startup time. Others
  368. may be added through the binding commands available in 
  369. X.B xc
  370. scripts.
  371. X
  372. X.TS
  373. tab(@) ;
  374. l2 l2 l
  375. l2B l2 l .
  376. Command@Function@Description
  377. X
  378. XXCAPE /@Help@Display the table of bound keys
  379. XXCAPE ?@Help@Display the table of bound keys
  380. X
  381. XXCAPE b@\fIB\fRREAK@Send a MODEM BREAK signal (a sustained null).
  382. X
  383. XXCAPE d@\fID\fRirectory@Display the phone directory.
  384. X
  385. XXCAPE f@\fIF\fRile@Send a file through the modem (ASCII transfer).
  386. X
  387. XXCAPE s@\fIS\fRcript@\fBXc\fR will request the name of a script to execute.
  388. X
  389. XXCAPE h@\fIH\fRangup@Tell the modem to go on-hook.
  390. X
  391. XXCAPE y\fR@Capture \fIY\fRes@T{
  392. Open the capture file in APPEND mode (text received over the port accumulates
  393. at the end of the file). If the file is already open, a message is printed to
  394. that effect.
  395. T}
  396. X
  397. XXCAPE n\fR@Capture \fIN\fRo@T{
  398. Close the capture file. If it wasn't open, a message of regret is printed.
  399. T}
  400. X
  401. XXCAPE x\fR@e\fIX\fRit@Exit terminal mode back to \fI<XC>\fR command mode.
  402. X
  403. XXCAPE q@\fIQ\fRuit@Quit \fBxc\fR altogether.
  404. X.TE
  405. X.SS \fIPhonelist\fR
  406. X.B Xc
  407. looks for a
  408. X.I .phonelist
  409. file in the following order: in the current directory, in your home directory
  410. as defined by the $HOME environment variable, or in the LIBDIR as defined
  411. at compile time in
  412. X.I xc.h.
  413. However in command mode, any filename can be substituted, using the SET PFILE
  414. command, so long as it has the following characteristics:
  415. X
  416. It is ASCII text (lines of text separated by newlines).
  417. X
  418. The first field of data in each line (after any whitespace and up to the next
  419. occurence of whitespace) is a phone number in a valid format for
  420. the modem being used.
  421. X
  422. Descriptive text may follow the phone number. Spaces may be included in this
  423. text, but a <TAB> must terminate this text.
  424. X
  425. The following three definitions may then follow in any order:
  426. X.TP 12
  427. PROTO=sss
  428. X(sss=7e2|7o2|8n1) Set the serial port protocol for 7-bit characters with
  429. either even or odd parity and two stop bits, or for 8-bit characters with
  430. no parity and one stop bit.
  431. X.TP
  432. BPS=nnnn
  433. X(n=300|1200|2400|9600|19200|38400) Set the bits/second rate to the
  434. specified value.
  435. X.TP
  436. SCRIPT=file
  437. Immediately after sending the autodial string, execute the script file
  438. specified. (Note that the specified filename is CASE SENSITIVE!)
  439. X.P
  440. The following definition, if used, must be the LAST field on the line.
  441. X
  442. PREFIX=xxxxxxxx (e.g., PREFIX=ATs110=0s111=0)
  443. X
  444. The PREFIX="string" allows you send your modem a different setup string for
  445. each situation (default: no special setup). This is helpful for MNP and Telebit
  446. modems which require special attention for contacting non-MNP modems and other
  447. Telebits.
  448. X.SS \fIScripts\fR
  449. The
  450. X.B xc
  451. script language is intended to be closely similar to the Unix Bourne shell
  452. language. Unfortunately, it isn't identical to the Bourne shell, so it has
  453. the same problem that the
  454. X.BR awk (C)
  455. program does for those experienced in the C language: an
  456. X.B awk
  457. script LOOKS like C, but it isn't, really; and in the same way, an
  458. X.B xc
  459. script LOOKS like a Bourne shell script, but isn't. So the operation of the
  460. Bourne shell, if you're familiar with it, is useful as an analogy in
  461. understanding the
  462. X.B xc
  463. script language, but only as an analogy.
  464. X
  465. An
  466. X.B xc
  467. script consists of lists of commands. Commands are set off from each other
  468. within a list by either a newline ('\\n') or a semicolon (;), as is the case
  469. with the Bourne shell. The semicolon and the newline have identical effect as
  470. command separators, so (anticipating a bit here) you could say either
  471. X
  472. X.nf
  473. X    if counter morethan 5
  474. X    then
  475. X       transmit "bye^M"
  476. X       quit
  477. X    endif
  478. X
  479. or
  480. X
  481. X    if counter morethan 5; then transmit "bye^M"; quit; endif
  482. X
  483. X.fi
  484. and the result will be the same either way. The newline and the semicolon
  485. are treated the same way as in the Bourne shell, except that a newline cannot
  486. be quoted so as to remove its interpretation as a command terminator (in
  487. other words, a quoted string cannot contain a newline).
  488. X
  489. Commands are composed of
  490. X.IR word s
  491. separated by spaces or tab characters, and a
  492. X.I word
  493. can be one of the following:
  494. X.TP 3
  495. X*
  496. One of the
  497. X.B xc
  498. script language keywords, which are listed below, with appropriate explanations.
  499. X.TP
  500. X*
  501. A number, meaning a sequence consisting only of digits, with
  502. an optional leading minus sign to indicate a negative number.
  503. X.TP
  504. X*
  505. A literal string of characters surrounded by double-quotation marks ('"'); such
  506. a string can be no longer than 80 characters. A double-quotation mark can be
  507. imbedded within the string by preceding it with a backslash ('\\"'); when the
  508. string is interpreted, the backslash is disregarded and the double-quotation
  509. mark is treated as part of the string. Mismatched quotation marks result in a
  510. syntax error.
  511. X.TP
  512. X*
  513. The name of a user variable, which can have either a string or a numeric value.
  514. The name of a user variable must begin with an alphabetic character.
  515. X.TP
  516. X*
  517. The name of a shell environment variable, preceded by a dollar sign ('$'). The
  518. X.B xc
  519. script language examines the Unix environment for the specified variable and,
  520. if such a variable exists, substitutes the value of that variable. The result
  521. is treated as if it were a quoted literal string, and, therefore, should not be
  522. more than 80 charac\%ters long, or else it will be truncated to its first 80
  523. characters. Similarly, such an environment variable should not contain a
  524. newline.
  525. X.TP
  526. X*
  527. An expression surrounded by back-quotation marks ('`'). This sort of expression
  528. operates similarly to the Bourne shell's command-substi\%tution mechanism: the
  529. contents of the back-quotes are passed to the Bourne shell, and the standard
  530. output of the back-quoted command is treated as if it were a quoted literal
  531. string. Therefore, the command's output should not be more than 80 characters
  532. long, nor contain a newline. Also, the contents of the back-quotes cannot be
  533. longer than 80 characters, nor contain a newline.
  534. X.TP
  535. X*
  536. An expression introduced by a pound-sign (or number-sign: '#'), which is
  537. treated as a comment. All characters from the '#' until the end of the
  538. physical line are ignored. This comment mechanism is the same as in the
  539. Bourne shell.
  540. X.TP
  541. X*
  542. A limitation of the script language is that only one character string can be
  543. passed as an argument to any of the script commands. The \fIbind_function\fR,
  544. X\fIbind_script\fR, and \fIbind_string\fR commands thus need to use a decimal
  545. digit to represent the key that is to be bound. The ASCII value of the the key
  546. is employed. As an example, Control-C is identified as 3, Control-Z is 26, the
  547. XESCAPE key is 27, a space is 32, the number "1" is 49; letters are
  548. X.RI case- in sensitive
  549. so that identifying the bound key as "65" or as "97" will always bind 
  550. X.I both 
  551. X"a" and "A" to the same command.
  552. X.P
  553. Quoted literal strings (and the two other mechanisms that act like quoted
  554. literal strings, shell environment variables and back-quoted shell commands)
  555. may be up to 80 characters long. All other words must be no longer than 16
  556. characters, and are treated case-independently (which is to say, uppercase
  557. is the same as lowercase); note, though, that the names of shell environment
  558. variables are case-dependent (uppercase must match uppercase and lowercase
  559. must match lowercase), because they are case-dependent in the shell.
  560. X
  561. Any word not recognizable within the foregoing categories is treated as the
  562. name of a new user variable. Such a word, if longer than 16 characters, is
  563. considered to be a syntax error.
  564. X
  565. User variables are created with the 'assign' script keyword, and may have
  566. either numeric or string values. The type of a user variable is determined
  567. by how it's created; if it's assigned to a string, it's a string variable,
  568. and if it's assigned to a number, it's a numeric variable. The value of any
  569. user variable can be changed with another 'assign' command, and numeric
  570. variables can be changed to string variables and vice-versa. Shell
  571. environment variables cannot be changed within an
  572. X.B xc
  573. script, but the value of a shell environment variable can be assigned to a user
  574. variable, and the value of the user variable can thereafter be changed.
  575. X
  576. Scripts are contained in ASCII text disk files, one script to a file. A
  577. script can invoke another script as a subroutine with the 'call' keyword; up
  578. to 5 scripts can be nested in this way at any single time.
  579. X
  580. With all this said, the following list of
  581. X.B xc
  582. script language commands should be comprehensible. The format "<something>"
  583. means that a token, or word-type, of the "something" type is meant rather than
  584. the literal sequence 'something'.
  585. X.SS \fIScript Language Commands\fR
  586. Note that all the commands are case-\fIin\fRsensitive!
  587. X.TP 4
  588. X.I affirm
  589. Syntax: affirm
  590. X
  591. Reads a string from the terminal, and returns TRUE if the string begins with
  592. X\&'y' or 'Y'; otherwise, returns FALSE.  Used in evaluating conditional
  593. expressions. The string must be terminated by a newline or carriage-return.
  594. X
  595. XExample:
  596. X
  597. X    echo -n "Continue (y/n)? "
  598. X    if affirm
  599. X    then
  600. X        continue
  601. X    else
  602. X        break
  603. X    endif
  604. X.TP
  605. X.I assign
  606. Syntax: assign <varname> eq <number>
  607. X        assign <varname> eq "string"
  608. X        assign <varname1> eq <varname2>
  609. X        assign <varname> eq <script-command>
  610. X
  611. Assigns to user variable <varname> the value following "eq"; if that value is a
  612. number, then <varname> becomes a numeric user variable; if that value is a
  613. string, then <varname> becomes a string user variable. If <varname> does not
  614. already exist as a user variable, it is created. Variable space is allocated
  615. dynamically, but running out of memory space for variables is unlikely. All
  616. variables are global across scripts that run at the same time via the 'call'
  617. keyword, and all variables vanish when a script, called directly from
  618. X.B xc
  619. as opposed to called from another script, exits. In other words, variable
  620. values are not static except during 'call' execution. Variable names cannot be
  621. longer than 8 characters. Successive 'assigns' are permissible, and the type of
  622. the variable changes according to the type of the value following "eq". A user
  623. variable is destroyed with the 'unassign' keyword.
  624. X
  625. If a variable is assigned the value of a script command, then it becomes a
  626. numeric variable with value TRUE or FALSE, depending on the status returned by
  627. the script command. If a variable is assigned the value of a back-quoted
  628. command, it becomes a string variable with the value of the first 80 characters
  629. of the back-quoted command. If a variable is assigned equal to an environment
  630. variable, it becomes a string variable with the value of the first 80
  631. characters of the value of the environment variable.
  632. X
  633. XExamples:
  634. X
  635. X    assign numvar eq 5
  636. X    assign strvar eq "This variable is a string"
  637. X    assign mydir eq $HOME
  638. X    assign numvar2 eq numvar
  639. X    assign strvar2 eq strvar
  640. X    assign numvar eq true
  641. X    assign today eq `date`; echo "today is " today
  642. X.TP
  643. X.I beep
  644. Syntax: beep
  645. X
  646. Sends a Control-G to the terminal. Useful for alerting the user that some event
  647. has occurred, for example a CONNECT after a lengthy redialing session.
  648. X.TP
  649. X.I bind_function
  650. Syntax: bind_function \fIcode\fR "function"
  651. X
  652. Bind the character identified by the number specified by \fIcode\fR to the
  653. X\fBxc\fR builtin function "function".
  654. X
  655. The "function" may be one of the following (case is ignored):
  656. X
  657. X    BRKCHAR  Send modem BREAK signal
  658. X    CAPTEND  Turn off terminal mode capture
  659. X    CAPTYES  Turn on terminal mode capture
  660. X    DIALCHR  Dial from phonelist
  661. X    DIVCHAR  Send file through modem
  662. X    DOSCRPT  Execute script file (prompts interactively)
  663. X    EMITSTR  Emit string
  664. X    ENDCHAR  Exit terminal mode
  665. X    HLPCHAR  Display terminal mode key bindings
  666. X    HUPCHAR  Hang up modem
  667. X    QUITCHR  Quit program
  668. X    SCRPCHR  Prompt for script file
  669. X
  670. XExample:
  671. X
  672. X    bind_function 26 "quitchr"
  673. X
  674. This binds Control-Z to quit the XC program.
  675. X.TP
  676. X.I bind_script
  677. Syntax: bind_script \fIcode\fR "scriptname"
  678. X
  679. Bind the character identified by the number specified by \fIcode\fR to execute
  680. the script named by "scriptname".
  681. X
  682. Upon termination of the script, the program will resume terminal mode, unless
  683. the "quit" script function was executed.
  684. X
  685. XExamples:
  686. X
  687. X    bind_script 18 "/usr/lib/xc/.rz"
  688. X
  689. This binds Control-R to execute the script /usr/lib/xc/.rz. The .rz script
  690. supplied with the source code contains:
  691. X
  692. X    tty "on"
  693. X    echo -n "What files are to be received? "
  694. X    read FILES
  695. X    transmit "sz -y "
  696. X    transmit FILES
  697. X    transmit "^M"
  698. X    echo "Starting ZMODEM Receive (rz -y)"
  699. X    pipe "rz -y"
  700. X
  701. Pressing \fBXCAPE\fR \fB^R\fR will ask for some file name(s), and start an
  702. X.B sz
  703. command on the remote system, and an
  704. X.B rz
  705. on the local system to receive the file(s).
  706. X
  707. X    bind_script 19 "/usr/lib/xc/.sz"
  708. X
  709. This binds Control-S to execute the script /usr/lib/xc/.sz. The .sz script
  710. supplied with the source code contains:
  711. X
  712. X    tty "on"
  713. X    echo -n "What files are to be sent? "
  714. X    read FILES
  715. X    echo "Starting ZMODEM send (sz -y " FILES ")"
  716. X    pipe "sz -y " FILES
  717. X
  718. Pressing \fBXCAPE\fR \fB^S\fR will ask for some file name(s), and then launch
  719. X.B sz
  720. on the current system.
  721. X.B Sz
  722. will itself start an
  723. X.B rz
  724. process on the remote system.
  725. X
  726. X.TP
  727. X.I bind_string
  728. Syntax: bind_string \fIcode\fR "string"
  729. X
  730. Bind the character identified by the number specified by \fIcode\fR to emit the
  731. string specified by "string".
  732. X
  733. The string is sent to the modem untranslated; eg, it is not examined for
  734. embedded terminal mode escapes.
  735. X
  736. XExample:
  737. X
  738. X    bind_string 49 "AAATZ^M"
  739. X
  740. This binds "1" to send the sequence to reset the modem.
  741. X.TP
  742. X.I break
  743. Syntax: break
  744. X
  745. XExits from the immediately enclosing 'while' loop. Identical to the C language
  746. X\&'break', and to the Bourne shell 'break' except that the
  747. X.B xc
  748. script language 'break' does not take a numeric argument. Don't confuse this
  749. with the script keyword 'xmitbrk', which sends a BREAK signal to the modem port.
  750. X.TP
  751. X.I call
  752. Syntax: call "scriptname"
  753. X
  754. Suspends execution of the current script, and attempts to load and run the
  755. specified scriptname. The scriptname must be a quoted literal string. There is
  756. no
  757. X.B xc
  758. analogue of the Bourne shell "exec" command; all subscripts in
  759. X.B xc
  760. are treated as sub-routines. All variables are global across subscripts, so if
  761. a subscript changes the value of a variable, then that change will remain in
  762. effect after return to the parent script. Shell environment variables cannot be
  763. changed by any
  764. X.B xc
  765. script.
  766. X.TP
  767. X.I capture
  768. Syntax: capture "on"
  769. X        capture "off"
  770. X
  771. Turns the file-capture function on or off. Note that the arguments must be
  772. quoted literal strings. Note also that when the script exits into terminal
  773. mode, the file-capture function is turned off. If you have 'set auto "on"',
  774. then you will begin capturing immediately upon entering, or returning to,
  775. terminal mode. If not, then you must manually type "\fBXCAPE\fR \fBY\fR" to
  776. start capturing when entering terminal mode.
  777. X.TP
  778. X.I continue
  779. Syntax: continue
  780. X
  781. Resumes execution at the top of the immediately enclosing 'while' loop.
  782. Identical to the C language 'continue' instruction, and to the Bourne shell
  783. X\&'continue' command except that no numeric argument is accepted.
  784. X.TP
  785. X.I debug
  786. Syntax: debug "on"
  787. X        debug "off"
  788. X
  789. If the 'debug' option is on, then
  790. X.B xc
  791. will make many parenthetical comments about what it's doing while it runs the
  792. script. These comments can sometimes be helpful in debugging script logic. Note
  793. that the argument must be a quoted literal string.
  794. X
  795. While debugging a script containing a password, turn this option off, then on
  796. again, to reduce the excitement-level of any colleagues collected circa your
  797. screen.
  798. X.TP
  799. X.I decr
  800. Syntax: decr <numeric-variable>
  801. X
  802. Decrements the value of the specified numeric user variable by 1. Useful in
  803. controlling loop execution. If the specified variable isn't numeric, or doesn't
  804. exist, a syntax error results.
  805. X.TP
  806. X.I dial
  807. Syntax: dial "number-string"
  808. X
  809. Dials the specified number, using modem-command strings defined in xc.h.
  810. X.TP
  811. X.I echo
  812. Syntax: echo "string" <variable> ...
  813. X        echo -n "string" <variable> ...
  814. X
  815. This command sends its arguments to the user's terminal. The number of
  816. arguments is optional, except that the total result may not exceed 80
  817. characters. Variables and back-quoted shell commands are expanded as necessary.
  818. X
  819. If the "-n" switch is present, then no carriage-return/newline sequence is
  820. appended to the output.
  821. X
  822. XExamples:
  823. X
  824. X    echo "The time and date are now " `date`
  825. X    echo "My terminal type is " $TERM
  826. X    echo "My terminal type is " $TERM " today."
  827. X
  828. Note that whitespace isn't echoed unless it's part of a quoted literal string.
  829. X.TP
  830. X.I exit
  831. Syntax: exit
  832. X
  833. Terminates execution of the current script. If a script reaches its end, it
  834. exits automatically, so 'exit' is useful mainly to terminate a script
  835. prematurely.
  836. X.TP
  837. X.I false
  838. Syntax: false
  839. X
  840. Same as the Unix 'false' command. Does nothing, but returns a FALSE status
  841. value. Useful within conditional expressions.
  842. X
  843. XExample:
  844. X
  845. X    if waitfor "CONNECT" 30 eq false
  846. X    then
  847. X        quit
  848. X    endif
  849. X
  850. Note that above example could be rewritten using the negating modifier "!":
  851. X
  852. X    if ! waitfor "CONNECT" 30
  853. X    then
  854. X        quit
  855. X    endif
  856. X
  857. and note too that the "!" must be separated from its argument by whitespace.
  858. X.TP
  859. X.I file
  860. Syntax: file <script-command>
  861. X
  862. The standard output of the specified script command is sent to the current
  863. capture file. If the "capture" option is not set, then an error message is
  864. displayed, but script execution continues.
  865. X
  866. XExamples:
  867. X
  868. X    file echo "--------- CUT HERE ----------"
  869. X
  870. Sends the output of the 'echo' command to the current capture file, provided
  871. that the "capture" option is now "on".
  872. X
  873. X    file echo `date`
  874. X
  875. Sends a timestamp to the current capture file, provided that the "capture"
  876. option is now "on". The same thing could have been done with
  877. X
  878. X   file shell "date"
  879. X
  880. X.TP
  881. X.I hangup
  882. Syntax: hangup
  883. X
  884. Tells the modem to go on-hook.
  885. X
  886. X.TP
  887. X.I if\ \ 
  888. Syntax: if <list1>; then <list2>; [ else <list3>; ] endif
  889. X
  890. If <list1> evaluates as TRUE, performs <list2>; otherwise, if <list3> is
  891. specified, performs <list3>; then resumes execution immediately following
  892. X\&'endif'. To accommodate those whose minds wander while writing scripts, 'fi'
  893. is an acceptable synonym for 'endif'.
  894. X
  895. XEach list may consist of any number of script commands separated by semicolons
  896. or newlines. The value of <list1> is determined by inclusively OR'ing the value
  897. of each directive in the list, so that if any of the directives in <list1>
  898. evaluates as TRUE, then so will <list1>. <list1> is performed in its entirety
  899. regardless of the value of any of its component commands.
  900. X
  901. The keywords 'then', 'else', and 'endif' (or 'fi') must be immediately preceded
  902. by command separators, either a semicolon or a newline, just as is the case in
  903. the Bourne shell.
  904. X
  905. XFor conditional evaluation in 'if' and 'while' constructions, the following
  906. comparators are available in addition to the script directives mentioned
  907. elsewhere:
  908. X
  909. X    <varname1> eq "string"
  910. X    <varname1> eq <number>
  911. X    <varname1> eq <varname2>
  912. X    <varname1>
  913. X    "string"
  914. X
  915. evaluates as TRUE if the value of user variable <varname1> is the same as that
  916. of a specified string or numeric constant or of a specified second variable
  917. name. If the variable name <varname1> is not followed by anything else, then
  918. the expression evaluates as TRUE if the variable is numeric and has a non-zero
  919. value, or if the variable is a string variable and has a non-zero length;
  920. otherwise, the expression evaluates as FALSE. Comparing a string variable to a
  921. numeric variable, or vice-versa, causes a syntax error.
  922. X
  923. If a conditional expression consists only of a quoted literal string, the
  924. expression evaluates as TRUE if the string's length is non-zero, and otherwise
  925. evaluates to FALSE. Because environment variables and back-quoted shell
  926. commands are treated as if their output/value were quoted literal strings, this
  927. allows direct testing of a shell command or of an environment for non-zero
  928. length. Nonexistent environment variables are treated as if they exist with the
  929. value "" (a string of zero length).
  930. X
  931. X    <varname1> neq "string"
  932. X    <varname1> neq <number>
  933. X    <varname1> neq <varname2>
  934. X
  935. evaluates as TRUE if the value of user variable <varname1> is not equal to that
  936. of a specified string or numeric constant or of a specified second variable
  937. name. Comparing a string variable to a numeric variable, or vice-versa, causes
  938. a syntax error.
  939. X
  940. X    <varname1> lessthan "string"
  941. X    <varname1> lessthan <number>
  942. X    <varname1> lessthan <varname2>
  943. X
  944. evaluates as TRUE if the value of user variable <varname1> is less than that of
  945. a specified string or numeric constant or of a specified second variable name.
  946. String variables are compared lexically according to ASCII value.
  947. X
  948. X    <varname1> morethan "string"
  949. X    <varname1> morethan <number>
  950. X    <varname1> morethan <varname2>
  951. X
  952. operates identically to 'lessthan', except in reverse.
  953. X
  954. The value of any conditional expression may be negated by preceding it with an
  955. exclamation point followed by a space or tab.
  956. X
  957. XExamples:
  958. X
  959. X    if counter eq 0; then break; endif;
  960. X    if var1 eq var2; then echo "identical"; endif
  961. X    if counter morethan 20; then break; endif;
  962. X    if counter lessthan 0; then break; endif;
  963. X    if ! counter; then echo "counter is " counter; endif
  964. X
  965. To perform a list if any of a set of conditions exist:
  966. X
  967. X    if counter morethan 5;
  968. X        counter eq brkvalue;    # a second comparator
  969. X    then break;
  970. X    endif;
  971. X
  972. i.e., perform the 'break' directive if the value of numeric user
  973. variable 'counter' is greater than the numeric constant 5, or if the value
  974. of 'counter' is equal to that of the user numeric variable 'brkvalue'.
  975. X.TP
  976. X.I incr
  977. Syntax: incr <numeric-variable>
  978. X
  979. Increments the value of the specified numeric user variable by 1. The
  980. opposite of 'decr'.
  981. X.TP
  982. X.I linked
  983. Syntax: linked
  984. X
  985. X\&'linked' is a pseudo-function that evaluates as TRUE if the current script has
  986. been invoked from the dialing directory, and as FALSE otherwise.
  987. X
  988. XExample:
  989. X
  990. X    if ! linked; then
  991. X        dial "5551212"
  992. X    endif
  993. X.TP
  994. X.I pause
  995. Syntax: pause <number>
  996. X
  997. Suspends execution for the specified <number> of SECONDS. Identical to Unix
  998. X"sleep."
  999. X.TP
  1000. X.I pipe
  1001. Syntax: pipe "<shell-command>"
  1002. X
  1003. The standard input and standard output of the specified shell command are
  1004. connected to the modem port, and the command is executed. This is the
  1005. equivalent of the command mode "$" command.
  1006. X
  1007. XExamples:
  1008. X
  1009. X    pipe "echo \\"\\177\\""
  1010. X
  1011. sends a DELETE character to the modem.
  1012. X
  1013. X    pipe "rz"
  1014. X
  1015. performs a file receive via ZMODEM, assuming that Chuck Forsberg's 'rz/sz'
  1016. programs reside on your system.
  1017. X
  1018. X.TP
  1019. X.I portname
  1020. Syntax: portname
  1021. X
  1022. This isn't a command, but a synonym for the current modem terminal device,
  1023. treated as if it were a quoted string. Useful if modems of differing types are
  1024. attached to different terminal lines and need different dialing or
  1025. initialization sequences. To compare the value of 'portname' to some other
  1026. string, you must first assign a user variable equal to 'portname'.
  1027. X
  1028. XExamples:
  1029. X
  1030. X    assign myport eq portname
  1031. X    if myport eq "/dev/tty01"
  1032. X    then
  1033. X        transmit "AT&E0^M"
  1034. X    endif
  1035. X.TP
  1036. X.I quit
  1037. Syntax: quit
  1038. X
  1039. XExits the script and terminates
  1040. X.B xc
  1041. entirely. Any LCKfile will be removed if possible and the
  1042. user's terminal will be restored to the state it was in when
  1043. X.B xc
  1044. was invoked.
  1045. X.TP
  1046. X.I read
  1047. Syntax: read <variable-name>
  1048. X
  1049. Takes a string from the user's keyboard and places it into the specified user
  1050. variable. Any previous value of the specified variable is discarded.
  1051. X.TP
  1052. X.I redial
  1053. Syntax: redial
  1054. X
  1055. Redials the number most recently dialed with the 'dial' command.
  1056. X.TP
  1057. X.I seen
  1058. Syntax: seen "string" <number>
  1059. X
  1060. XEvaluates as TRUE if "string" has occurred within the last <number> characters
  1061. received during the most recent 'waitfor' command. Only up to 2048 characters
  1062. are remembered at any one time during 'waitfor' processing. If no <number> is
  1063. specified, then all the characters received during the most recent 'waitfor'
  1064. command are examined, up to a maximum of 2048. The 'seen' buffer is reset at
  1065. the beginning of each 'waitfor' command. This is useful to tell which of
  1066. several strings has been received.
  1067. X
  1068. XExample:
  1069. X
  1070. X    if waitfor "string1" 20
  1071. X    then
  1072. X        echo "Received 'string1'."
  1073. X    else
  1074. X        if seen "string2"
  1075. X        then
  1076. X            echo "Received 'string2' instead."
  1077. X        endif
  1078. X    endif
  1079. X.TP
  1080. X.I set\ 
  1081. Syntax: set
  1082. X.RB < xc -set-option>
  1083. X<value>
  1084. X
  1085. This command is the same as the command-mode
  1086. X.B xc
  1087. X\&'set' command, such as "set bps 1200", "set cis on", and so forth, except that
  1088. a string <value> must be enclosed within double-quotation marks.
  1089. X
  1090. XExamples:
  1091. X
  1092. X    set cis "on"
  1093. X    set cfile "newfilename"
  1094. X    set auto "on"
  1095. X    set bps 2400
  1096. X.TP
  1097. X.I shell
  1098. Syntax: shell "<shell-command>"
  1099. X
  1100. The shell command enclosed within the double-quotation marks is executed. This
  1101. is similar to the
  1102. X.B xc
  1103. command-mode "!" command. Remember that if the shell command contains
  1104. double-quotation marks, they must be escaped with backslashes.
  1105. X.TP
  1106. X.I timeout
  1107. Syntax: timeout <number>
  1108. X
  1109. If <number> is greater than zero, starts a timer which will cause the MOST
  1110. DEEPLY NESTED script to exit when <number> of MINUTES expire. If <number> is
  1111. zero, then any pending timeout is cancelled. If <number> is negative, nothing
  1112. happens.
  1113. X
  1114. XExpiration of the specified timeout causes the most deeply nested script to
  1115. exit, not to terminate
  1116. X.BR xc .
  1117. To cause the program to quit if a timeout expires, use a subscript.
  1118. X
  1119. XExample:
  1120. X
  1121. X    'script1' contains:
  1122. X
  1123. X        call "script2"
  1124. X        if expired
  1125. X        then
  1126. X            quit
  1127. X        endif
  1128. X        # more commands
  1129. X
  1130. X
  1131. X    'script2' contains:
  1132. X
  1133. X        assign expired eq 1
  1134. X        timeout 5           # limit of 5 minutes
  1135. X        while ! waitfor "login:" 30
  1136. X        do
  1137. X            xmitbrk
  1138. X        done
  1139. X        assign expired eq 0
  1140. X        exit
  1141. X
  1142. When 'script2' exits, the numeric variable 'expired' will be set to 1 if
  1143. X\&'script2' timed out, and will be 0 otherwise. 'script1' can act on this
  1144. information accordingly.
  1145. X.TP
  1146. X.I transmit
  1147. Syntax: transmit "string"
  1148. X
  1149. Sends a string to the modem. The string is sent with brief pauses between
  1150. characters, to accommodate modems that cannot accept rapid command input, and
  1151. within the string, any alphabetic character preceded by a caret (^) is
  1152. translated to the corresponding Control-character.
  1153. X
  1154. XExample:
  1155. X
  1156. X    transmit "ATDT 5551212^M"
  1157. X
  1158. sends the string "ATDT 5551212" to the modem, followed by a carriage-return
  1159. character.
  1160. X.TP
  1161. X.I true
  1162. Syntax: true
  1163. X
  1164. Does nothing, but always evaluates as TRUE. Useful in conditional
  1165. expressions. The opposite of 'false'.
  1166. X.TP
  1167. X.I tty\ 
  1168. Syntax: tty "on"
  1169. X        tty "off"
  1170. X
  1171. Ordinarily, during script execution, characters received from the modem port
  1172. are echoed to the user's terminal screen. This happens only during 'waitfor'
  1173. and 'type' execution, so it may be a bit choppy. This echoing can be turned
  1174. off with
  1175. X
  1176. X    tty "off"
  1177. X
  1178. and turned back on with
  1179. X
  1180. X    tty "on"
  1181. X
  1182. Note that "on" and "off" must be enclosed in quotation marks.
  1183. X.TP
  1184. X.I type
  1185. Syntax: type "<filename>"
  1186. X
  1187. Sends the specified ASCII file to the modem port. This is the same as the
  1188. X.B xc
  1189. terminal-mode "send ASCII file" escape.
  1190. X.TP
  1191. X.I unassign
  1192. Syntax: unassign <variablename>
  1193. X
  1194. XErases the specified user variable. The variable may be either numeric or
  1195. string type. The variable name must not be enclosed in quotation marks, because
  1196. variable names are considered to be
  1197. X.B xc
  1198. script keywords, and not literal strings.
  1199. X.TP
  1200. X.I waitfor
  1201. Syntax: waitfor "string" <number>
  1202. X
  1203. Scans input from the modem port for an occurrence of the specified string,
  1204. which must be enclosed in quotation marks. The scanning continues for the
  1205. specified <number> of SECONDS or until the specified string is identified in
  1206. the modem input stream, whichever comes first. This command evaluates as TRUE
  1207. if the specified string is found, and as FALSE if the specified <number> of
  1208. SECONDS elapses and the string isn't found within that time. The default time,
  1209. if no <number> is specified, is roughly 30 seconds.
  1210. X
  1211. String matching is performed on a case-\fIin\fRsensitive basis.
  1212. Within the string, any alphabetic character preceded by a caret (^) is
  1213. translated to the corresponding Control-character.
  1214. X
  1215. XExamples:
  1216. X
  1217. X    assign counter eq 1
  1218. X    while ! waitfor "login:" 15
  1219. X    do
  1220. X        xmitbrk; incr counter; if counter morethan 5
  1221. X        then
  1222. X            quit
  1223. X        endif
  1224. X    done
  1225. X
  1226. If in a CompuServe Forum the "prompt character" has been set by the user to
  1227. be a backspace, this test will log off if the main prompt is not seen in the
  1228. next sixty seconds:
  1229. X
  1230. X    if ! waitfor "forum !^H" 60
  1231. X    then
  1232. X        transmit "bye^M"; quit
  1233. X    endif
  1234. X
  1235. If the 'cis' option has been set to "on", either in the
  1236. X.I .xc
  1237. startup script, or by a direct 'set' command from command mode, or with
  1238. X
  1239. X    set cis "on"
  1240. X
  1241. within a current script, and if during 'waitfor' processing a CIS B-Plus
  1242. protocol file transfer request is received, then the B-Plus protocol transfer
  1243. is performed, the <number> argument is reset to its original value, and
  1244. X\&'waitfor' processing continues. This allows automatic B-Plus protocol file
  1245. transfers from within a script.
  1246. X.TP
  1247. X.I while
  1248. Syntax: while <list1>; do <list2>; done
  1249. X
  1250. Operates similarly to the 'if' command, except that <list2> is executed
  1251. repeatedly so long as <list1> evaluates as TRUE. All the conditional
  1252. comparators and rules for comparisons that apply for the 'if' command also
  1253. apply to 'while'. (Note that 'while' loops can be nested within 'if' commands
  1254. and vice-versa.)
  1255. X.TP
  1256. X.I xmitbrk
  1257. Syntax: xmitbrk
  1258. X
  1259. Sends a BREAK signal to the modem port.
  1260. X.SS \fIFile Transfers\fR
  1261. When transferring files using the XMODEM protocol, the file mode is specified
  1262. in the upload/download command. A TEXT mode transfer enables translation of the
  1263. transmitted or received file to support CP/M and MS-DOS end-of-line characters.
  1264. When transmitting a file using TEXT mode, all newlines are converted to
  1265. carriage-return/newline sequences. When receiving a file using TEXT mode, all
  1266. carriage-return/newline sequences are converted to just a newline. A BINARY mode
  1267. transfer transmits the file "as is" without any such conversion.
  1268. X
  1269. When transferring files using CompuServe B-Plus protocol, the format of the
  1270. file is specified by the host. An ASCII mode will force
  1271. X.B xc
  1272. to perform TEXT mode translation; a BINARY mode will not do any translation.
  1273. This means that, in either direction, a BINARY mode will send bytes "as is",
  1274. whereas in ASCII mode, an incoming file will always be stripped of end-of-line
  1275. carriage-returns, while an ASCII file being uploaded may or may not have
  1276. carriage-returns added after each newline, depending on the "on" or "off"
  1277. setting of the "cr" option.
  1278. X
  1279. When using either the "%t" (take) command, an XMODEM receive command, or a
  1280. CompuServe B-Plus download command,
  1281. X.B xc
  1282. will check if the file name you specify already exists on your system, and ask
  1283. for an OK to overwrite the file, or for an alternative name. In the CompuServe
  1284. case, there will also be an option to "Resume" a download. If the CRC checksum
  1285. of the bytes that you already have in the file matches CompuServe's checksum on
  1286. the the same initial bytes in its version of the file, file transfer will
  1287. proceed from that point onwards. This saves connect time when a very large
  1288. download has been interrupted during a prior session.
  1289. X
  1290. Script-driven file transfers using the CompuServe B-Plus protocol are more or
  1291. less built into the
  1292. X.B xc
  1293. script language, since during 'waitfor' processing, a file transfer request
  1294. from the modem port will trigger a B-Plus transfer if the "cis" mode is set.
  1295. The difficulty in performing such transfers isn't in the transfer itself, but
  1296. rather in the maneuvering required to get into position to transfer the correct
  1297. file, and is something that probably only experienced script writers should
  1298. wrestle with. However, if we assume that in the midst of a script, you've
  1299. reached a point where you can issue a "download" command to CompuServe, for
  1300. instance a "Disposition" prompt during a File Library "BROWSE" command, and you
  1301. want to download the present file, which is "XCALL.C" on CompuServe, and
  1302. X"xcall.c" does not exist in your current working directory, you'd use the
  1303. following sequence of script commands to do it:
  1304. X
  1305. X    set cis on       # can't do auto file B-Plus transfer otherwise
  1306. X    transmit "dow^M"
  1307. X    pause 2          # wait for CIS to display protocol menu
  1308. X    transmit "6^M"   # B-Plus is number 6 on that menu
  1309. X    transmit "xcall.c^M"  # "file name for your computer:"
  1310. X    waitfor "Disposition"
  1311. X
  1312. During the final "waitfor" processing, the CompuServe ENQ character will
  1313. be recognized and the transfer will proceed automatically. Then 'waitfor'
  1314. will continue waiting for the "Disposition" prompt, after which your script
  1315. can proceed. The same sort of thing can be done with file uploads, but once
  1316. again, the difficulty isn't with the transfer, but rather with setting things
  1317. up so that the transfer will be requested at the correct place.
  1318. X
  1319. A shell script,
  1320. X.B cisdownload
  1321. provided with the distribution, can automatically retrieve a single file from a
  1322. CompuServe library.
  1323. X
  1324. XFor XMODEM, YMODEM, and ZMODEM transfers via scripts, there's no mechanism
  1325. built into the
  1326. X.B xc
  1327. script language to use the built-in, 128-byte-packet XMODEM in
  1328. X.BR xc .
  1329. Instead, we strongly recommend that you obtain Chuck Forsberg's excellent,
  1330. shareware utility called "RZSZ", which can handle XMODEM, XMODEM-1K,
  1331. YMODEM, and ZMODEM transfers and which can be used from within
  1332. X.B xc
  1333. with the 'pipe' command, or from command mode with the '$' command, or using
  1334. the examples under the 
  1335. X.I bind_script
  1336. command above.
  1337. X.SH Exit Codes
  1338. X.TS
  1339. tab(@) ;
  1340. c2B lw(60) .
  1341. X0@Successful, uneventful completion.
  1342. X
  1343. X1@Error in command-line arguments.
  1344. X
  1345. X2@Failure in forking to execute a command or run a shell.
  1346. X
  1347. X3@T{
  1348. No modem port specified on the command line, or contained in the environment
  1349. variable MODEM.
  1350. T}
  1351. X
  1352. X4@Inability to create a LCK..file for the specified port.
  1353. X
  1354. X5@Inability to open the specified port.
  1355. X
  1356. X6@No environment variable TERM has been set.
  1357. X
  1358. X7@No entry for the current TERM setting in /etc/termcap.
  1359. X
  1360. X8@Problem caused by the 'ungetty' program.
  1361. X.TE
  1362. X.SH Ccpyright
  1363. X.B Xc
  1364. and its source files and sample scripts and manual page are Copyright 1993 by
  1365. Jean-Pierre Radley.
  1366. X
  1367. Permission is granted to the public to use this code in any manner, without
  1368. any warranty, implied or otherwise, of fitness for a particular purpose.
  1369. X
  1370. By virtue of a restriction previously placed upon all code derivative from
  1371. X.BR xcomm ", the " xc
  1372. code and associated files may not be sold by anyone to anyone, nor incorporated
  1373. into any product that is not also free. It's OK to transfer them for free.
  1374. X.SH Authors
  1375. This manual page was written by Fred Buck (1989) and Jean\-Pierre Radley
  1376. X(1990, 1991, 1992, 1993).
  1377. X.B Xc
  1378. itself is the product of many synergistic wise minds. See the README document.
  1379. X.SH Version
  1380. This edition of the manual is for XC \*(]V.
  1381. END_OF_FILE
  1382. if test 45748 -ne `wc -c <'xc.nro'`; then
  1383.     echo shar: \"'xc.nro'\" unpacked with wrong size!
  1384. fi
  1385. # end of 'xc.nro'
  1386. fi
  1387. if test -f 'xcscrpt.c' -a "${1}" != "-c" ; then 
  1388.   echo shar: Will not clobber existing file \"'xcscrpt.c'\"
  1389. else
  1390. echo shar: Extracting \"'xcscrpt.c'\" \(44712 characters\)
  1391. sed "s/^X//" >'xcscrpt.c' <<'END_OF_FILE'
  1392. X/*    xcscrpt.c -- script interpreter module for XC");
  1393. X    This file uses 4-character tabstops
  1394. X    Author: larry gensch, December 1987
  1395. X    Major rewrite: fred buck, Jan 1989
  1396. X    Binding code: larry gensch, September 1991
  1397. X    This code is released to the public domain
  1398. X*/
  1399. X
  1400. X#include <stdio.h>
  1401. X#include <string.h>
  1402. X#include <sys/types.h>
  1403. X#include <sys/times.h>
  1404. X#include <sys/param.h>
  1405. X#include <ctype.h>
  1406. X#include <signal.h>
  1407. X#include <setjmp.h>
  1408. X#include <sys/stat.h>
  1409. X#include "xc.h"
  1410. X
  1411. jmp_buf            here;
  1412. extern FILE        *cfp;
  1413. short            ttyflag, debugflag, linkflag, scriptflag,
  1414. X                mrbstart,    /* ring buffer start pointer */
  1415. X                mrbcount,    /* ring buffer counter */
  1416. X                BREAK = 0;    /* a hook for a later 'trap' keyword */
  1417. extern short    eofflag;
  1418. extern int        redial(), s_set(), s_exit(), xmitbrk();
  1419. extern void        divert(), set_onoff(), xcdial();
  1420. X
  1421. int                my_escape = 1;    /* Control-A; resettable at run-time */
  1422. X
  1423. char            mringbuf[LG_BUFF];    /* ring buffer for modem input */
  1424. static            S_abort();
  1425. static void        unsetall(), S_bombout(), S_call();
  1426. static char        *NO_ARG = "%s command must have an argument";
  1427. X
  1428. static bindfunc_t   find_function(); /* bindfunc_t : see xc.h */
  1429. X
  1430. typedef struct bindstruct {
  1431. X    int                    bs_c;        /* Character prefix */
  1432. X    int                    bs_function;/* Function code */
  1433. X    char                *bs_string;    /* String/script to emit */
  1434. X    struct bindstruct    *bs_next;    /* Pointer to next entry */
  1435. X} binding_t;
  1436. static binding_t *first_binding = NIL(binding_t);
  1437. X
  1438. typedef struct {
  1439. X    bindfunc_t    bf_function;/* Function code */
  1440. X    char        *bf_name;    /* bind_function() name */
  1441. X    char        *bf_string;    /* String/script assigned */
  1442. X} bindstr_t;
  1443. X
  1444. static bindstr_t function_list[] = {
  1445. X    {ENDCHAR, "endchar", "Exit terminal mode"},
  1446. X    {QUITCHR, "quitchr", "Quit program"},
  1447. X    {CAPTYES, "captyes", "Turn on terminal mode capture"},
  1448. X    {CAPTEND, "captend", "Turn off terminal mode capture"},
  1449. X    {DIVCHAR, "divchar", "Send file through modem"},
  1450. X    {DIALCHR, "dialchr", "Dial from phonelist"},
  1451. X    {HUPCHAR, "hupchar", "Hang up modem"},
  1452. X    {SCRPCHR, "scrpchr", "Prompt for script file"},
  1453. X    {HLPCHAR, "hlpchar", "Display terminal mode key bindings"},
  1454. X    {BRKCHAR, "brkchar", "Send modem BREAK signal"},
  1455. X    {EMITSTR, "emitstr", "Emit string"},
  1456. X    {DOSCRPT, "doscrpt", "Execute script file"}
  1457. X};
  1458. X
  1459. X#define FUNCTION_COUNT (sizeof(function_list) / sizeof(function_list[0]))
  1460. X
  1461. static void 
  1462. newsigint(junk)
  1463. int junk;
  1464. X{
  1465. X    eofflag++;
  1466. X    show_abort();
  1467. X    S_bombout();
  1468. X    longjmp(here,1);
  1469. X}
  1470. X
  1471. void 
  1472. do_script(file)
  1473. char *file;
  1474. X{
  1475. X    void (*oldvec)();
  1476. X
  1477. X    capture = eofflag = FALSE;
  1478. X    ttyflag = scriptflag = TRUE;
  1479. X
  1480. X    oldvec = signal(SIGINT, newsigint);
  1481. X    if (!setjmp(here))
  1482. X        S_call(file);
  1483. X
  1484. X    unsetall();
  1485. X    if (capture)
  1486. X        capture = FALSE,
  1487. X        fclose(cfp);
  1488. X
  1489. X    linkflag = scriptflag = FALSE;
  1490. X
  1491. X    signal(SIGINT, oldvec);
  1492. X}
  1493. X
  1494. static 
  1495. k_seen(bytes,fword)
  1496. long bytes;
  1497. char *fword;
  1498. X{
  1499. X    int i, j, k;
  1500. X    char *cptr;
  1501. X
  1502. X    cptr = fword;
  1503. X
  1504. X    if (!fword || !*fword)
  1505. X        sprintf(Msg,NO_ARG,"SEEN"),
  1506. X        S2(Msg);
  1507. X
  1508. X    j = mrbstart - 1;
  1509. X    if (bytes<=0 || bytes>LG_BUFF)
  1510. X        bytes = LG_BUFF;
  1511. X    k = mrbcount - bytes;    /* check only most recent 'bytes' bytes */
  1512. X    i = 0;
  1513. X    while ((i++)<mrbcount){
  1514. X        ++j;
  1515. X        j = j % LG_BUFF;
  1516. X        if (i<k)
  1517. X            continue;
  1518. X        if (mringbuf[j] != *cptr){
  1519. X            cptr = fword;
  1520. X            continue;
  1521. X        }
  1522. X        if (*(++cptr)=='\0')
  1523. X            return SUCCESS;
  1524. X    }
  1525. X    return FAILURE;
  1526. X}
  1527. X
  1528. k_waitfor(interval,fword)
  1529. long interval;
  1530. char *fword;
  1531. X{
  1532. X    register c, i = -1 ;
  1533. X    register long limit, waitfor_msec = 0;
  1534. X    char *ptr;
  1535. X    struct tms tbuf;
  1536. X
  1537. X    mrbstart = mrbcount = 0;
  1538. X    sprintf(line,"\"%s\"",fword);
  1539. X    lptr = line;
  1540. X    getword();
  1541. X    lc_word(word);
  1542. X    ptr = word;
  1543. X
  1544. X    if (interval < -1){
  1545. X        waitfor_msec = -interval;
  1546. X        goto SPITOUT;
  1547. X    }
  1548. X    if (!fword || word[0] == '\0'){
  1549. X        sprintf(Msg,NO_ARG,"WAITFOR");
  1550. X        S2(Msg);
  1551. X        return FAILURE;
  1552. X    }
  1553. X
  1554. X    waitfor_msec = 1000 * ((interval > 0) ? interval : 30);
  1555. X    eofflag = FALSE;
  1556. X
  1557. SPITOUT: limit = times(&tbuf) + (HZ * waitfor_msec)/1000;
  1558. X    while (limit >= times(&tbuf) && !eofflag){
  1559. X        if ((c = readbyte(1)) == -1)
  1560. X            continue;
  1561. X
  1562. X        if (cismode && c==ENQ){
  1563. X            s_cis();
  1564. X            goto SPITOUT;
  1565. X        }
  1566. X
  1567. X        ++i;
  1568. X        i = i % LG_BUFF;
  1569. X        mringbuf[i] = c;
  1570. X        mrbstart = mrbstart % LG_BUFF;
  1571. X        if (mrbcount<LG_BUFF)
  1572. X            ++mrbcount;
  1573. X        else
  1574. X            ++mrbstart,
  1575. X            mrbstart = mrbstart % LG_BUFF;
  1576. X
  1577. X        if (ttyflag)
  1578. X            fputc(c,tfp);
  1579. X
  1580. X        if (capture && c != '\r')
  1581. X            fputc(c,cfp);
  1582. X
  1583. X        if (tolower(c) != *ptr){
  1584. X            ptr = word;
  1585. X            continue;
  1586. X        }
  1587. X
  1588. X        if (*++ptr == '\0')
  1589. X            return SUCCESS;
  1590. X    }
  1591. X    return FAILURE;
  1592. X}
  1593. X
  1594. static 
  1595. k_transmit(junk,fword)
  1596. long junk;
  1597. char *fword;
  1598. X{
  1599. X    sprintf(line,"\"%s\"",fword);
  1600. X    lptr = line;
  1601. X    getword();
  1602. X    if (!fword || word[0] == '\0'){
  1603. X        sprintf(Msg,NO_ARG,"TRANSMIT");
  1604. X        S2(Msg);
  1605. X        return FAILURE;
  1606. X    }
  1607. X    send_string(word);
  1608. X    return SUCCESS;
  1609. X}
  1610. X
  1611. static 
  1612. k_pause(pause_time,junk)
  1613. long pause_time;
  1614. char *junk;
  1615. X{
  1616. X    pause_time = pause_time ? pause_time : 5;
  1617. X    sleep((unsigned)pause_time);
  1618. X    return SUCCESS;
  1619. X}
  1620. X
  1621. static 
  1622. k_dial(junk,fword)
  1623. long junk;
  1624. char *fword;
  1625. X{
  1626. X    sprintf(line,"%s",fword);
  1627. X    lptr = line;
  1628. X    getword();
  1629. X    if (!fword || word[0] == '\0'){
  1630. X        sprintf(Msg,NO_ARG,"DIAL");
  1631. X        S2(Msg);
  1632. X        return FAILURE;
  1633. X    }
  1634. X    xcdial(word);
  1635. X    return SUCCESS;
  1636. X}
  1637. X
  1638. static 
  1639. k_capture(junk,fword)
  1640. long junk;
  1641. char *fword;
  1642. X{
  1643. X    int val = capture;
  1644. X
  1645. X    sprintf(word,"capture");
  1646. X    sprintf(line,"%s",fword);
  1647. X    lptr = line;
  1648. X    set_onoff(&capture);
  1649. X
  1650. X    if (val == capture)
  1651. X        return SUCCESS;
  1652. X
  1653. X    if (!capture)
  1654. X        fclose(cfp);
  1655. X    else {
  1656. X        if (!(cfp = fopen(captfile, "a"))){
  1657. X            sprintf(Msg,"Can't open capture file %s",captfile);
  1658. X            S2(Msg);
  1659. X            eofflag++;
  1660. X            return FAILURE;
  1661. X        }
  1662. X        setbuf(cfp,NIL(char));
  1663. X    }
  1664. X    return SUCCESS;
  1665. X}
  1666. X
  1667. static 
  1668. k_debug(junk,fword)
  1669. long junk;
  1670. char *fword;
  1671. X{
  1672. X    sprintf(word,"debug");
  1673. X    sprintf(line,"%s",fword);
  1674. X    lptr = line;
  1675. X    set_onoff(&debugflag);
  1676. X    return SUCCESS;
  1677. X}
  1678. X
  1679. static 
  1680. k_tty(junk,fword)
  1681. long junk;
  1682. char *fword;
  1683. X{
  1684. X    sprintf(word,"tty");
  1685. X    sprintf(line,"%s",fword);
  1686. X    lptr = line;
  1687. X    set_onoff(&ttyflag);
  1688. X    return SUCCESS;
  1689. X}
  1690. X
  1691. static 
  1692. k_type(junk,fword)
  1693. long junk;
  1694. char *fword;
  1695. X{
  1696. X    sprintf(line,"%s",fword);
  1697. X    lptr = line;
  1698. X    getword();
  1699. X    if (!fword || word[0] == '\0'){
  1700. X        sprintf(Msg,NO_ARG,"TYPE");
  1701. X        S2(Msg);
  1702. X        return FAILURE;
  1703. X    }
  1704. X    divert(TRUE);
  1705. X    return SUCCESS;
  1706. X}
  1707. X
  1708. static 
  1709. k_linked()
  1710. X{
  1711. X    return linkflag;
  1712. X}
  1713. X
  1714. X/*    unbind_key() removes the binding attached to the keycode specified by (c).*/
  1715. static void 
  1716. unbind_key(c)
  1717. int c;
  1718. X{
  1719. X    binding_t *ptr = first_binding, *prev = NIL(binding_t);
  1720. X    if (!ptr)
  1721. X        return;
  1722. X
  1723. X    while (ptr) {
  1724. X        if (ptr->bs_c == c) {
  1725. X            if (ptr->bs_string)
  1726. X                free(ptr->bs_string);
  1727. X            if (prev)
  1728. X                prev->bs_next = ptr->bs_next;
  1729. X            else
  1730. X                first_binding->bs_next = ptr->bs_next;
  1731. X            free(ptr);
  1732. X            return;
  1733. X        }
  1734. X        prev = ptr;
  1735. X        ptr = ptr->bs_next;
  1736. X    }
  1737. X}
  1738. X
  1739. X/*    bind_key() binds the key whose ASCII code is specified by (c) to the
  1740. X    function whose code is specified by (function). Emit strings
  1741. X    (for EMITSTR) and script names (for DOSCRPT) are specified by (string).
  1742. X*/
  1743. static void 
  1744. bind_key(c,function,string)
  1745. int c;
  1746. int function;
  1747. char *string;
  1748. X{
  1749. X    binding_t *ptr = (binding_t *) malloc(sizeof(binding_t)), *curr, *prev;
  1750. X
  1751. X    if (!ptr) {
  1752. X            S2("BIND_KEY allocation error");
  1753. X            S_abort();
  1754. X    }
  1755. X
  1756. X    unbind_key(c);
  1757. X
  1758. X    ptr->bs_c = c;
  1759. X    ptr->bs_function = function;
  1760. X    ptr->bs_string = strdup(string);
  1761. X
  1762. X    /* The following is an insertion sort to ensure that the bindings are
  1763. X       stored in ascending sequence by key code.  This makes
  1764. X       show_bindings()'s display easier to read. -lg
  1765. X    */
  1766. X
  1767. X    for (prev = NIL(binding_t), curr = first_binding;
  1768. X        curr != NIL(binding_t);
  1769. X        prev = curr, curr = curr->bs_next) {
  1770. X        if (ptr->bs_c < curr->bs_c) {
  1771. X            if (!prev) {
  1772. X                ptr->bs_next = first_binding;
  1773. X                first_binding = ptr;
  1774. X                break;
  1775. X            } else {
  1776. X                ptr->bs_next = curr;
  1777. X                prev->bs_next = ptr;
  1778. X                break;
  1779. X            }
  1780. X        }
  1781. X    }
  1782. X
  1783. X    if (curr == NIL(binding_t)) {
  1784. X        if (!prev)
  1785. X            first_binding = ptr;
  1786. X        else
  1787. X            prev->bs_next = ptr;
  1788. X        ptr->bs_next = NIL(binding_t);
  1789. X    }
  1790. X}
  1791. X
  1792. X/*    bind_function() Binds the key whose code is represented by the value in
  1793. X    (n) to execute the XC builtin function whose name is pointed to be (cptr).
  1794. X    Any previous binding of the specified keycode is forgotten.
  1795. X    This routine is designed to be an XC script builtin.
  1796. X*/
  1797. static 
  1798. bind_function(n,cptr)
  1799. long n;
  1800. char *cptr;
  1801. X{
  1802. X    int c;
  1803. X    bindfunc_t code;
  1804. X
  1805. X    if (n < 1L || n > 127L) {
  1806. X        sprintf(Msg,"Invalid key code %d in BIND_FUNCTION command",n);
  1807. X        S2(Msg);
  1808. X        return FAILURE;
  1809. X    }
  1810. X
  1811. X    c = tolower((int) n);
  1812. X
  1813. X    sprintf(line, "\"%s\"", cptr);
  1814. X    lptr = line;
  1815. X    getword();
  1816. X    if (cptr && cptr[0] != '\0') {
  1817. X        lc_word(word);
  1818. X        code = find_function(word);
  1819. X        if (code == BADFUNC) {
  1820. X            sprintf(Msg,"Invalid function name '%s' in BIND_FUNCTION command",
  1821. X            word);
  1822. X            S2(Msg);
  1823. X            return FAILURE;
  1824. X        }
  1825. X        bind_key(c, code, word);
  1826. X    } else {
  1827. X        sprintf(Msg,NO_ARG,"BIND_FUNCTION");
  1828. X        S2(Msg);
  1829. X        return FAILURE;
  1830. X    }
  1831. X
  1832. X    return SUCCESS;
  1833. X}
  1834. X
  1835. X/*    bind_string() binds the key whose code is represented by the
  1836. X    decimal value in (n) to emit the string pointed to by (cptr).
  1837. X    Any previous binding of the specified keycode is forgotten.
  1838. X    This routine is designed to be an XC script builtin.
  1839. X*/
  1840. static 
  1841. bind_string(n,cptr)
  1842. long n;
  1843. char *cptr;
  1844. X{
  1845. X    int c;
  1846. X
  1847. X    if (n < 1L || n > 127L) {
  1848. X        sprintf(Msg,"Invalid key code %d in BIND_STRING command",n);
  1849. X        S2(Msg);
  1850. X        return FAILURE;
  1851. X    }
  1852. X
  1853. X    c = tolower((int) n);
  1854. X
  1855. X    sprintf(line, "\"%s\"", cptr);
  1856. X    lptr = line;
  1857. X    getword();
  1858. X    if (cptr && cptr[0] != '\0')
  1859. X        bind_key(c, EMITSTR, word);
  1860. X    else {
  1861. X        sprintf(Msg,NO_ARG,"BIND_STRING");
  1862. X        S2(Msg);
  1863. X        return FAILURE;
  1864. X    }
  1865. X
  1866. X    return SUCCESS;
  1867. X}
  1868. X
  1869. X/*    bind_script() binds the key whose code is represented by the value in
  1870. X    (n) to execute the XC script whose name is pointed to by (cptr).
  1871. X    Any previous binding of the specified keycode is forgotten.
  1872. X    This routine is designed to be an XC script builtin.
  1873. X*/
  1874. static 
  1875. bind_script(n,cptr)
  1876. long n;
  1877. char *cptr;
  1878. X{
  1879. X    int c;
  1880. X
  1881. X    if (n < 1L || n > 127L) {
  1882. X        sprintf(Msg,"Invalid key code %d in BIND_STRING command",n);
  1883. X        S2(Msg);
  1884. X        return FAILURE;
  1885. X    }
  1886. X
  1887. X    c = tolower((int) n);
  1888. X
  1889. X    sprintf(line, "\"%s\"", cptr);
  1890. X    lptr = line;
  1891. X    getword();
  1892. X    if (cptr && cptr[0] != '\0')
  1893. X        bind_key(c, DOSCRPT, word);
  1894. X    else {
  1895. X        sprintf(Msg,NO_ARG,"BIND_SCRIPT");
  1896. X        S2(Msg);
  1897. X        return FAILURE;
  1898. X    }
  1899. X
  1900. X    return SUCCESS;
  1901. X}
  1902. X
  1903. X/*    Variables Section */
  1904. X/*    Most of the variable-handling logic is credit: Steve Manes 1987 */
  1905. X#define VNAMELEN    16    /* maximum name length for variables */
  1906. X#define VMAXSIZE    256    /* maximum length for CHAR variable */
  1907. X#define VMAXVARS    30    /* maximum number of user variables */
  1908. X#define VCHAR        'C'    /* CHARACTER variable type */
  1909. X#define VNUM        'N'    /* NUMERIC variable type (always 'long') */
  1910. X
  1911. X/* Variable structure */
  1912. typedef struct var {
  1913. X    char name[VNAMELEN+1];    /* variable name */
  1914. X    struct var    *next;        /* ptr to next structure in var_list */
  1915. X    char type;                /* variable type */
  1916. X    union {                    /* pointer to CHAR or NUM/DATE */
  1917. X        char str[VMAXSIZE+1];
  1918. X        long num;
  1919. X    } u;
  1920. X} VAR;
  1921. X
  1922. static VAR    *Varlist = NIL(VAR);    /* top of variable list */
  1923. static VAR    *Lastvar = NIL(VAR);    /* bottom of variable list */
  1924. X
  1925. X/* Valid variable name characters */
  1926. unchar 
  1927. OKname[]= {
  1928. X     /* control characters */
  1929. X      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  1930. X     /* ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ */
  1931. X        1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,
  1932. X     /* A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` */
  1933. X        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,
  1934. X     /* a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~       */
  1935. X        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0
  1936. X};
  1937. X
  1938. X/*    Return pointer to VAR structure for User or System variable
  1939. X    'name', otherwise return NIL(VAR).
  1940. X    Variable contents in vp->u.[str|num]
  1941. X*/
  1942. static VAR *
  1943. findvar(name)
  1944. char *name;
  1945. X{
  1946. X    static    VAR *vp;    /* pointer to output structure */
  1947. X
  1948. X    if (!name || !*name || !Varlist)
  1949. X        return NIL(VAR);
  1950. X    vp = Varlist;
  1951. X    while (vp){
  1952. X        if (!strncmp(name, vp->name, VNAMELEN) )
  1953. X            return vp;
  1954. X        vp = vp->next;
  1955. X    }
  1956. X    return NIL(VAR);                /* not found */
  1957. X}
  1958. X
  1959. X/*    Delete User variable 'name' from VAR list.
  1960. X    If variable doesn't exist, no error is returned
  1961. X*/
  1962. static void 
  1963. unsetvar(name)
  1964. char *name;
  1965. X{
  1966. X    VAR *p;
  1967. X    VAR *lastp;
  1968. X
  1969. X    if (!name || !*name)
  1970. X        return;
  1971. X    for (p=Varlist; p; lastp = p, p = p->next){
  1972. X        if (!strncmp(name, p->name, VNAMELEN) ){    /* name match? */
  1973. X            if (Varlist == Lastvar)                    /* only 1 variable */
  1974. X                Varlist = Lastvar = NIL(VAR);        /* in list */
  1975. X            else if (p == Varlist)                    /* first variable */
  1976. X                Varlist = p->next;
  1977. X            else {
  1978. X                lastp->next = p->next;                /* dump variable in middle
  1979. X                                                       of list */
  1980. X                if (p == Lastvar)                    /* or last object */
  1981. X                    Lastvar = lastp;                /* in list */
  1982. X            }
  1983. X            free(p);            /* reclaim memory */
  1984. X            break;
  1985. X        }
  1986. X    }
  1987. X}
  1988. X
  1989. X/*    Set the value of User variable 'name' to 'val' with 'type'.
  1990. X    If variable exists, change its contents. Otherwise, create it.
  1991. X    Returns: FAILURE or SUCCESS
  1992. X*/
  1993. static 
  1994. setvar(name,type,str,val)
  1995. char *name;
  1996. char type;
  1997. char *str;
  1998. long *val;
  1999. X{
  2000. X    VAR *vp;
  2001. X    short i;
  2002. X
  2003. X    if (!name || !*name)
  2004. X        return FAILURE;
  2005. X    if (!(vp = findvar(name))){ /* create new variable */
  2006. X        for (i=0; i < VNAMELEN && name[i]; i++){
  2007. X            if( !OKname[(name[i] & 0x7F)] || isdigit(name[0]) ){
  2008. X                sprintf(Msg,"Illegal variable name '%s'",name);
  2009. X                S_abort();
  2010. X            }
  2011. X        }
  2012. X        if (!(vp = (VAR *)malloc(sizeof(VAR)))){
  2013. X            sprintf(Msg,"%s: allocation error",name);
  2014. X            S2(Msg);
  2015. X            return FAILURE;
  2016. X        }
  2017. X        lc_word(name);
  2018. X        strncpy(vp->name, name, VNAMELEN);    /* set vari name */
  2019. X        vp->next = NIL(VAR);                /* flag 'no next' */
  2020. X        if (!Varlist)
  2021. X            Varlist = vp;                    /* first variable */
  2022. X        else
  2023. X            Lastvar->next = vp;                /* add this to the list */
  2024. X        Lastvar = vp;                        /* set 'last' pointer */
  2025. X    }
  2026. X
  2027. X    if (type == VCHAR)
  2028. X        strncpy(vp->u.str, str, VMAXSIZE);
  2029. X    else
  2030. X        vp->u.num = *val;
  2031. X    vp->type = type;
  2032. X    return SUCCESS;
  2033. X}
  2034. X
  2035. X/*    Unset all user variables, deallocating memory.
  2036. X    No error returned
  2037. X*/
  2038. static void 
  2039. unsetall()
  2040. X{
  2041. X    VAR *p;
  2042. X    VAR *nextp;
  2043. X
  2044. X    if (!Varlist)
  2045. X        return;
  2046. X    p = Varlist;
  2047. X    while (p->next){
  2048. X        nextp = p->next;
  2049. X        free(p);
  2050. X        p = nextp;
  2051. X    }
  2052. X    Varlist = Lastvar = NIL(VAR);
  2053. X}
  2054. X/*    end variables section */
  2055. X
  2056. X/*    Action Primitives */
  2057. static struct {
  2058. X    char *name;
  2059. X    int (*funcptr)();
  2060. X} s_acttab[] = {
  2061. X    {"beep",        beep},
  2062. X    {"bind_function",    bind_function},
  2063. X    {"bind_script",    bind_script},
  2064. X    {"bind_string",    bind_string},
  2065. X    {"capture",        k_capture},
  2066. X    {"debug",        k_debug},
  2067. X    {"dial",        k_dial},
  2068. X    {"hangup",        hangup},
  2069. X    {"linked",        k_linked},
  2070. X    {"pause",        k_pause},
  2071. X    {"quit",        s_exit},
  2072. X    {"redial",        redial},
  2073. X    {"seen",        k_seen},
  2074. X    {"xmitbrk",        xmitbrk},
  2075. X    {"transmit",    k_transmit},
  2076. X    {"tty",            k_tty},
  2077. X    {"type",        k_type},
  2078. X    {"waitfor",        k_waitfor},
  2079. X    {NIL(char),            0}
  2080. X};
  2081. X/*    end of primitives */
  2082. X
  2083. X/*    token types */
  2084. typedef enum {
  2085. X    NULLTOK,    /* terminating '\0' in script buffer */
  2086. X    ACTION,        /* an action (primitive or script cmd) */
  2087. X    AFFIRM,        /* script 'affirm' */
  2088. X    BACKQUOT,    /* script command substitution */
  2089. X    SBREAK,        /* script 'break' */
  2090. X    CALL,        /* script 'call' */
  2091. X    COMMENT,    /* comment */
  2092. X    SCONTNUE,    /* script 'continue' */
  2093. X    DECR,        /* script 'decr' */
  2094. X    DO,            /* script 'do' */
  2095. X    DONE,        /* script 'done' */
  2096. X    ECHOS,        /* script 'echo' */
  2097. X    EFLAG,        /* '-n' switch for script 'echo' cmd */
  2098. X    ELSE,        /* script 'else' */
  2099. X    ENDIF,        /* script 'endif' */
  2100. X    ENDTRAP,    /* script 'endtrap' */
  2101. X    EQ,            /* operator "equals" */
  2102. X    EXIT,        /* script 'exit' */
  2103. X    SFILE,        /* script 'file' */
  2104. X    SFALSE,        /* script 'false' */
  2105. X    IF,            /* script 'if' */
  2106. X    INCR,        /* script 'incr' */
  2107. X    LESSTHAN,    /* operator "less than" */
  2108. X    LITERAL,    /* a literal string (e.g. "abcde") */
  2109. X    MORETHAN,    /* operator "greater than" */
  2110. X    NEGATE,        /* negation operator for comparisons */
  2111. X    NEQ,        /* operator "not equal to" */
  2112. X    NUMBER,        /* a numeric constant (e.g. 12345) */
  2113. X    PIPE,        /* script 'pipe' */
  2114. X    READ,        /* script 'read' */
  2115. X    SHELL,        /* script 'shell' */
  2116. X    SET,        /* script 'assign' */
  2117. X    STRAP,        /* script 'trap' */
  2118. X    TERMINAT,    /* statement terminators (';' and '\n') */
  2119. X    THEN,        /* script 'then' */
  2120. X    STRUE,        /* script 'true' */
  2121. X    UNSET,        /* script 'unset' */
  2122. X    UNTRAP,        /* script 'untrap' */
  2123. X    XCSET,        /* xc 'set' command */
  2124. X    VARNAME,    /* a variable name */
  2125. X    WHILE,        /* script 'while' */
  2126. X    TTERROR,    /* unrecognizable token */
  2127. X    TIMEOUT        /* script 'timeout' */
  2128. X} TOK_TYPE;
  2129. X
  2130. X/*    token table */
  2131. static struct {
  2132. X    char *name;
  2133. X    TOK_TYPE token;
  2134. X} s_toktab[] = {
  2135. X    {"NULLTOK",        NULLTOK},
  2136. X    {"ACTION",        ACTION},
  2137. X    {"affirm",        AFFIRM},
  2138. X    {"BACKQUOT",    BACKQUOT},
  2139. X    {"break",        SBREAK},
  2140. X    {"call",        CALL},
  2141. X    {"COMMENT",        COMMENT},
  2142. X    {"continue",    SCONTNUE},
  2143. X    {"decr",        DECR},
  2144. X    {"do",            DO},
  2145. X    {"done",        DONE},
  2146. X    {"echo",        ECHOS},
  2147. X    {"-n",            EFLAG},
  2148. X    {"else",        ELSE},
  2149. X    {"endif",        ENDIF},
  2150. X    {"fi",            ENDIF},
  2151. X    {"ENDTRAP",        ENDTRAP},
  2152. X    {"eq",            EQ},
  2153. X    {"exit",        EXIT},
  2154. X    {"false",        SFALSE},
  2155. X    {"file",        SFILE},
  2156. X    {"if",            IF},
  2157. X    {"incr",        INCR},
  2158. X    {"lessthan",    LESSTHAN},
  2159. X    {"LITERAL",        LITERAL},
  2160. X    {"morethan",    MORETHAN},
  2161. X    {"!",            NEGATE},
  2162. X    {"neq",            NEQ},
  2163. X    {"NUMBER",        NUMBER},
  2164. X    {"pipe",        PIPE},
  2165. X    {"read",        READ},
  2166. X    {"assign",        SET},
  2167. X    {"shell",        SHELL},
  2168. X    {"TRAP",        STRAP},        /* 'trap' keyword left for later dev't */
  2169. X    {"TERMINAT",    TERMINAT},
  2170. X    {"then",        THEN},
  2171. X    {"timeout",        TIMEOUT},
  2172. X    {"true",        STRUE},
  2173. X    {"unassign",    UNSET},
  2174. X    {"UNTRAP",        UNTRAP},
  2175. X    {"set",            XCSET},
  2176. X    {"VARNAME",        VARNAME},
  2177. X    {"while",        WHILE},
  2178. X    {"\0",            TTERROR}
  2179. X};
  2180. X/*    end token types */
  2181. X
  2182. X/*    tok_value is set by lexan() in the following instances:
  2183. X     TOK_TYPE NUMBER:     (long) value of number
  2184. X     TOK_TYPE LITERAL:     pointer to beginning of quoted string
  2185. X     TOK_TYPE ACTION:     function pointer to appropriate vector
  2186. X     TOK_TYPE VARNAME:     pointer to VAR struct
  2187. X     TOK_TYPE TTERROR:     pointer to strange construction in script
  2188. X    All other values of TOK_TYPE don't require further information.
  2189. X*/
  2190. static union {
  2191. X    long numval;        /* numbers */
  2192. X    char *strptr;        /* for literal strings (points to initial '"') */
  2193. X    int (*funcptr)();    /* vector for primitives */
  2194. X    VAR *varptr;        /* for variables */
  2195. X} tok_value;
  2196. X
  2197. X
  2198. X/*    lexan() is the lexical analyzer, which translates words
  2199. X    into token types and sets tok_value appropriately. It's
  2200. X    called repeatedly by the language parser, S_parse().
  2201. X*/
  2202. static TOK_TYPE
  2203. lexan(pcptr)
  2204. char **pcptr;            /* address of script program counter */
  2205. X{
  2206. X    long nvalue, negpos = 1;
  2207. X    VAR *varptr;
  2208. X    FILE *bqpipe;
  2209. X    int i, c;                /* really a char, but 'int' to spot EOF */
  2210. X    static char *cptr, *lasttok,
  2211. X                latoken[VNAMELEN+1], temp[VMAXSIZE+1], bqcmd[VMAXSIZE+1];
  2212. X    extern FILE *popen();
  2213. X
  2214. X    /* if in debug mode, echo script line to tfp */
  2215. X    if (debugflag && *pcptr>lasttok){
  2216. X        cptr = *pcptr - 1;
  2217. X        while (*cptr==' ' || *cptr=='\t') --cptr;
  2218. X        if (*cptr=='\n'){
  2219. X            fputs("+ ",tfp);
  2220. X            ++cptr;
  2221. X            while (*cptr!='\n' && *cptr)
  2222. X                fputc(*(cptr++),tfp);
  2223. X            fputc('\r',tfp),
  2224. X            fputc('\n',tfp);
  2225. X        }
  2226. X    }
  2227. X
  2228. X    /* skip to beginning of next token */
  2229. X    while (**pcptr==' ' || **pcptr=='\t') ++(*pcptr);
  2230. X    tok_value.strptr = cptr = lasttok = *pcptr;                /* save place */
  2231. X
  2232. X                                    /* negation operator for comparisons */
  2233. X    if (*cptr=='!' && (*(cptr+1)==' ' || *(cptr+1)=='\t')){
  2234. X        ++cptr;
  2235. X        *pcptr = cptr;
  2236. X        return NEGATE;
  2237. X    }
  2238. X                                    /* comment in script */
  2239. X    if (*cptr=='#'){
  2240. X        while (*cptr && *cptr!='\n') ++cptr;
  2241. X        *pcptr = cptr;
  2242. X        return TERMINAT;
  2243. X    }
  2244. X                                    /* statement terminator */
  2245. X    if (*cptr==';' || *cptr=='\n'){
  2246. X        ++cptr;
  2247. X        *pcptr = cptr;
  2248. X        return TERMINAT;
  2249. X    }
  2250. X                                    /* end of script */
  2251. X    if (*cptr=='\0')
  2252. X        return NULLTOK;
  2253. X                                    /* quoted literal string */
  2254. X    if (*cptr=='"'){
  2255. X        ++cptr;
  2256. X        while (*cptr && *cptr!='\n' && !(*cptr=='"' && *(cptr-1)!='\\'))
  2257. X            ++cptr;
  2258. X        if (*cptr=='"'){
  2259. X            ++cptr;
  2260. X            *pcptr = cptr;
  2261. X            return LITERAL;
  2262. X        }
  2263. X        sprintf(Msg,"Unmatched quote");
  2264. X        S_abort();
  2265. X    }
  2266. X                            /* environment variable (treat as a literal) */
  2267. X    if (*cptr=='$'){
  2268. X        ++cptr;
  2269. X        for (i=0; i<VMAXSIZE; ++i){
  2270. X            if (!*cptr || *cptr==' ' || *cptr=='\t' || *cptr=='\n'
  2271. X                || *cptr=='\r' || *cptr==';')
  2272. X                    break;
  2273. X            temp[i] = *(cptr++);
  2274. X        }
  2275. X        temp[i] = '\0';
  2276. X        tok_value.strptr = getenv(temp);
  2277. X        if (!tok_value.strptr)
  2278. X            tok_value.strptr = temp,
  2279. X            sprintf(Msg,"%s: no such environment variable",temp),
  2280. X            S2(Msg),
  2281. X            tok_value.strptr = "";
  2282. X        *pcptr = cptr;
  2283. X        return LITERAL;
  2284. X    }
  2285. X                        /* back-quoted shell command (treat like env var) */
  2286. X    if (*cptr=='`'){
  2287. X        ++cptr;
  2288. X        i = 0;
  2289. X        while (*cptr && *cptr!='\n' && *cptr!='`' && (++i)<VMAXSIZE)
  2290. X            ++cptr;
  2291. X        if (*cptr=='`'){
  2292. X            for (i=0; i<VMAXSIZE; ++i){            /* tok_value ptr points */
  2293. X                bqcmd[i] = tok_value.strptr[i+1];    /* to leading '`' */
  2294. X                if (bqcmd[i]=='`'){
  2295. X                    bqcmd[i] = '\0';
  2296. X                    break;
  2297. X                }
  2298. X            }
  2299. X            bqcmd[i] = '\0';
  2300. X            signal(SIGCLD,SIG_DFL);
  2301. X            if (!(bqpipe=popen(bqcmd,"r"))){
  2302. X                sprintf(Msg,"%s: cannot create pipe",bqcmd);
  2303. X                S_abort();
  2304. X            }
  2305. X            else {
  2306. X                temp[0] = '\0';
  2307. X                i = 0;
  2308. X                while (i<=VMAXSIZE && (c=fgetc(bqpipe))!=EOF && c!='\n')
  2309. X                    temp[i++] = c;
  2310. X                fflush(bqpipe);
  2311. X                pclose(bqpipe);
  2312. X                temp[i] = '\0';
  2313. X                tok_value.strptr = temp;
  2314. X                *pcptr = cptr + 1;
  2315. X                return LITERAL;
  2316. X            }
  2317. X        }
  2318. X        else {
  2319. X            sprintf(Msg,"Unmatched back-quote:");
  2320. X            S_abort();
  2321. X        }
  2322. X    }
  2323. X                                /* dialout port name */
  2324. X    if (!strncmp(cptr,"portname",8)){
  2325. X        tok_value.strptr =mport(NIL(char));
  2326. X        *pcptr += 8;
  2327. X        return LITERAL;
  2328. X    }
  2329. X                                /* leading hyphen, maybe a negative number? */
  2330. X    if (*cptr=='-')
  2331. X        negpos = (-1),
  2332. X        ++cptr;
  2333. X                                /* string beginning with a digit */
  2334. X    if (isdigit(*cptr)){
  2335. X        nvalue = (*cptr - '0') * negpos;
  2336. X        while (*(++cptr)){
  2337. X            if (isdigit(*cptr)){
  2338. X                nvalue *= 10;
  2339. X                nvalue += (*cptr - '0');
  2340. X                if (nvalue>0) nvalue *= negpos;
  2341. X                continue;
  2342. X            }
  2343. X            else if (strchr(" \t\n;",*cptr)){
  2344. X                tok_value.numval = nvalue;
  2345. X                *pcptr = cptr;
  2346. X                return NUMBER;
  2347. X            }
  2348. X            sprintf(Msg,"Variable name cannot begin with a digit: ");
  2349. X            S_abort();
  2350. X        }
  2351. X        tok_value.numval = nvalue;
  2352. X        *pcptr = cptr;
  2353. X        return NUMBER;
  2354. X    }
  2355. X                    /* check for '-n' switch for echo (type EFLAG) */
  2356. X    if (negpos<0){
  2357. X        if (*cptr=='N' || *cptr=='n'){
  2358. X            while (*cptr && !strchr(" \t\n;",*cptr)) ++cptr;
  2359. X            *pcptr = cptr;
  2360. X            return EFLAG;
  2361. X        }
  2362. X        sprintf(Msg,"Bad option to ECHO command");
  2363. X        S_abort();
  2364. X    }
  2365. X                            /* impermissible initial character */
  2366. X    if (!isalpha(*cptr)){
  2367. X        sprintf(Msg,"bad initial character: %c", *cptr);
  2368. X        S_abort();
  2369. X    }
  2370. X
  2371. X        /* remember that tok_value.strptr points to start of token */
  2372. X    for (i=1; i<(VNAMELEN+1); ++i){        /* jump to next field separator */
  2373. X        ++cptr;
  2374. X        if (*cptr=='\0' || strchr(" \t\n;",*cptr))
  2375. X            break;
  2376. X    }
  2377. X    if (i>VNAMELEN){                        /* word too long */
  2378. X        sprintf(Msg,"Variable name too long");
  2379. X        S_abort();
  2380. X    }
  2381. X    strncpy(latoken,tok_value.strptr,i);    /* copy word to array 'latoken' */
  2382. X    latoken[i] = '\0';
  2383. X    lc_word(latoken);                        /* cvt to lowercase */
  2384. X                                            /* script keywords */
  2385. X                    /* scan table for keyword match */
  2386. X    for (i=0; *(s_toktab[i].name) && strcmp(latoken,s_toktab[i].name); ++i)
  2387. X        ;
  2388. X    if (*s_toktab[i].name)    {                /* lc keyword recognized */
  2389. X        if (s_toktab[i].token==STRUE)
  2390. X            tok_value.numval = TRUE;
  2391. X        if (s_toktab[i].token==SFALSE)
  2392. X            tok_value.numval = FALSE;
  2393. X        *pcptr = cptr;
  2394. X        return s_toktab[i].token;
  2395. X    }
  2396. X                                    /* system primitive (ACTION) */
  2397. X                                    /* scan table for keyword match */
  2398. X    for (i=0;s_acttab[i].name && strcmp(latoken,s_acttab[i].name);++i)
  2399. X        ;
  2400. X    if (s_acttab[i].name){            /* primitive recognized */
  2401. X        tok_value.funcptr = s_acttab[i].funcptr;
  2402. X        *pcptr = cptr;
  2403. X        return ACTION;
  2404. X    }
  2405. X                                    /* user variable name */
  2406. X    if ((varptr=findvar(latoken))){    /* existing user variable */
  2407. X        tok_value.varptr = varptr;
  2408. X        *pcptr = cptr;
  2409. X        return VARNAME;
  2410. X    }
  2411. X                /* could this be the name of a new variable? */
  2412. X    tok_value.strptr = latoken;    /* just in case this is 'on', 'off', etc. */
  2413. X    if (!setvar(latoken,VCHAR,"",0))    /* can't create it */
  2414. X        return TTERROR;
  2415. X    if (!(varptr=findvar(latoken)))    /* can't retrieve it */
  2416. X        return TTERROR;
  2417. X    else {                            /* got it */
  2418. X        tok_value.varptr = varptr;
  2419. X        *pcptr = cptr;
  2420. X        return VARNAME;
  2421. X    }
  2422. X}
  2423. X
  2424. X/*        utility routines called by S_parse() */
  2425. X
  2426. X/*    S_affirm is a placeholder. It's the function to get a yes or no
  2427. X    response from the user.
  2428. X*/
  2429. static 
  2430. S_affirm()
  2431. X{
  2432. X    char c, junk;
  2433. X
  2434. X    c = getchar();
  2435. X    fputc(c,tfp);
  2436. X    while ((junk=getchar()) !='\n' && junk !='\r')
  2437. X        fputc(junk,tfp);
  2438. X
  2439. X    fputc('\r',tfp),
  2440. X    fputc('\n',tfp);
  2441. X    return(c=='y' || c=='Y');
  2442. X}
  2443. X
  2444. X/*    S_addsub increments or decrements a numeric variable.
  2445. X    It assumes that since the INCR or DECR directives call
  2446. X    lexan() for the variable just before coming here, the
  2447. X    tok_value structure contains a pointer to the variable
  2448. X    whose value is to be changed.
  2449. X*/
  2450. static 
  2451. S_addsub(direction)
  2452. int direction;
  2453. X{
  2454. X    long oldval = tok_value.varptr->u.num;
  2455. X    oldval += direction;
  2456. X
  2457. X    if (!setvar(tok_value.varptr->name,VNUM,"",&oldval)){
  2458. X        sprintf(Msg,"Error setting variable '%s'", tok_value.varptr->name);
  2459. X        S_abort();
  2460. X    }
  2461. X    return(tok_value.varptr->u.num ? SUCCESS : FAILURE);
  2462. X}
  2463. X
  2464. X/*    S_qstrip returns a pointer to a (static) string with leading and
  2465. X    trailing double-quote marks removed. If the string happens to
  2466. X    lack a leading or trailing double-quote mark, then the string
  2467. X    will be returned with its beginning unchanged, and its length
  2468. X    equal to VMAXSIZE or the length of the string, whichever is
  2469. X    shorter. Double-quote marks escaped with a backslash are included
  2470. X    in the returned string and the backslash is excised.
  2471. X*/
  2472. static char *
  2473. S_qstrip(strptr)
  2474. char *strptr;
  2475. X{
  2476. X    int i;
  2477. X    static char strbuf[VMAXSIZE+2];
  2478. X
  2479. X    if (*strptr=='"')
  2480. X        ++strptr;
  2481. X    for (i=0; i<VMAXSIZE+1; ++i){
  2482. X        if (*strptr=='\\' && *(strptr+1)=='"' && *(strptr-1)!='\\')
  2483. X            ++strptr;
  2484. X        if ((*strptr=='"' && *(strptr-1)!='\\') || !*strptr || *strptr=='\n')
  2485. X            break;
  2486. X        strbuf[i] = *strptr;
  2487. X        ++strptr;
  2488. X    }
  2489. X    strbuf[i] = '\0';
  2490. X    return strbuf;
  2491. X}
  2492. X
  2493. X/*    S_read does the parsing grunts for the script 'read' directive.    On
  2494. X    entry, the 'read' token has been parsed, but that's all.
  2495. X*/
  2496. static 
  2497. S_read(pcptr)
  2498. char **pcptr;
  2499. X{
  2500. X    int i;
  2501. X    VAR *varptr1;
  2502. X    static char strbuf[VMAXSIZE+2];
  2503. X
  2504. X    if (lexan(pcptr)!=VARNAME)
  2505. X        S_abort();
  2506. X    varptr1 = tok_value.varptr;
  2507. X    strbuf[0] = '\0';
  2508. X    for (i=0; i<VMAXSIZE; ++i){
  2509. X        strbuf[i] = getchar();
  2510. X        if (strbuf[i]==BS){
  2511. X            if (i>0)
  2512. X                fputs("\b \b",tfp),
  2513. X                i -= 2;
  2514. X            else
  2515. X                i = -1;
  2516. X            continue;
  2517. X        }
  2518. X        fputc(strbuf[i],tfp);
  2519. X        if (strbuf[i]=='\n' || strbuf[i]=='\r'){
  2520. X            strbuf[i] = '\0';
  2521. X            break;
  2522. X        }
  2523. X    }
  2524. X    strbuf[VMAXSIZE] = '\0';
  2525. X    fputc('\r',tfp),
  2526. X    fputc('\n',tfp);
  2527. X    return(setvar(varptr1->name,VCHAR,strbuf,0));
  2528. X}
  2529. X
  2530. X/*    S_perform invokes a system primitive and returns SUCCESS if the
  2531. X    primitive succeeds, FAILURE if it fails. On entry, only the ACTION
  2532. X    token has been parsed.
  2533. X*/
  2534. static 
  2535. S_perform(pcptr)
  2536. char **pcptr;
  2537. X{
  2538. X    int (*fptr)();
  2539. X    char *cptr;
  2540. X    VAR *varptr1;
  2541. X    long n = -1;
  2542. X
  2543. X    fptr = tok_value.funcptr;
  2544. X    cptr = NIL(char);
  2545. X    while (TRUE){
  2546. X        switch (lexan(pcptr)){
  2547. X            case TERMINAT:
  2548. X                break;
  2549. X            case NUMBER:
  2550. X                if (n != -1)
  2551. X                    S_abort();
  2552. X                n = tok_value.numval;
  2553. X                continue;
  2554. X            case LITERAL:
  2555. X            case TTERROR:
  2556. X                cptr = S_qstrip(tok_value.strptr);
  2557. X                continue;
  2558. X            case VARNAME:
  2559. X                varptr1 = tok_value.varptr;
  2560. X                if (varptr1->type==VCHAR){
  2561. X                        cptr = varptr1->u.str;
  2562. X                        break;
  2563. X                }
  2564. X                if (n != -1)
  2565. X                    S_abort();
  2566. X                n = varptr1->u.num;
  2567. X                continue;
  2568. X            default:
  2569. X                S_abort();
  2570. X        }
  2571. X        break;
  2572. X    }
  2573. X    return (*fptr)(n,cptr);
  2574. X}
  2575. X
  2576. X/*    S_set does the parsing grunts for the script 'assign' directive. It's
  2577. X    a separate function mostly to keep from cluttering up S_parse()
  2578. X    too much.    On entry, only the 'set' token has been received from
  2579. X    the directive containing it.
  2580. X*/
  2581. static
  2582. S_set(pcptr)
  2583. char **pcptr;                /* S_parse()'s program counter (p_pc) */
  2584. X{
  2585. X    TOK_TYPE nexttype;
  2586. X    VAR *varptr1, *varptr2;
  2587. X    char *setstr;
  2588. X
  2589. X    if ((nexttype=lexan(pcptr))!=VARNAME)
  2590. X        S_abort();
  2591. X    varptr1 = tok_value.varptr;
  2592. X    if ((nexttype=lexan(pcptr))!=EQ)
  2593. X        S_abort();
  2594. X    switch (nexttype = lexan(pcptr)){
  2595. X        case LITERAL:
  2596. X            setstr = S_qstrip(tok_value.strptr);
  2597. X            return(setvar(varptr1->name,VCHAR,setstr,0));
  2598. X        case ACTION:
  2599. X        case AFFIRM:
  2600. X            if (nexttype==ACTION)
  2601. X                tok_value.numval = (long) S_perform(pcptr);
  2602. X            else
  2603. X                tok_value.numval = (long) S_affirm();
  2604. X        case NUMBER:
  2605. X        case STRUE:
  2606. X        case SFALSE:
  2607. X            return(setvar(varptr1->name,VNUM,"",&tok_value.numval));
  2608. X        case VARNAME:
  2609. X            varptr2 = tok_value.varptr;
  2610. X            switch (varptr2->type){
  2611. X                case VCHAR:
  2612. X                    return(setvar(varptr1->name,VCHAR,varptr2->u.str,0));
  2613. X                default:
  2614. X                    return(setvar(varptr1->name,VNUM,"",&(varptr2->u.num)));
  2615. X            }
  2616. X        default:
  2617. X            S_abort();
  2618. X    }
  2619. X}
  2620. X
  2621. X/*    S_varcmp() compares a variable's value with a string or numeric
  2622. X    literal, or with the value of a second variable. Once again,
  2623. X    this function does parsing grunts for S_parse().
  2624. X*/
  2625. static
  2626. S_varcmp(varptr1, pcptr)
  2627. VAR *varptr1;
  2628. char **pcptr;
  2629. X{
  2630. X    TOK_TYPE compmode;
  2631. X    long testnum;
  2632. X    static char strbuf[VMAXSIZE+1];
  2633. X    char *cmpstr;
  2634. X    int status, numvar;
  2635. X    VAR *varptr2;
  2636. X
  2637. X    numvar = (varptr1->type!=VCHAR);
  2638. X    compmode = lexan(pcptr);
  2639. X    switch (compmode){
  2640. X        case EQ:
  2641. X        case NEQ:
  2642. X        case MORETHAN:
  2643. X        case LESSTHAN:
  2644. X            break;
  2645. X        case TERMINAT:
  2646. X            if (numvar)
  2647. X                return(varptr1->u.num ? TRUE : FALSE);
  2648. X            else
  2649. X                return(*varptr1->u.str ? TRUE : FALSE);
  2650. X        default:
  2651. X            S_abort();
  2652. X    }
  2653. X    switch (lexan(pcptr)){
  2654. X        case LITERAL:
  2655. X            if (numvar){
  2656. X                sprintf(Msg,"Error: %s is a numeric variable",varptr1->name);
  2657. X                S_abort();
  2658. X            }
  2659. X            ++tok_value.strptr;
  2660. X            strncpy(strbuf,tok_value.strptr,VMAXSIZE);
  2661. X            *(strchr(strbuf,'"')) = '\0';
  2662. X            cmpstr = strbuf;
  2663. X            break;
  2664. X        case NUMBER:
  2665. X        case STRUE:
  2666. X        case SFALSE:
  2667. X            if (!numvar){
  2668. X                sprintf(Msg,"Error: %s is a string variable",varptr1->name);
  2669. X                S_abort();
  2670. X            }
  2671. X            testnum = tok_value.numval;
  2672. X            break;
  2673. X        case VARNAME:
  2674. X            varptr2 = tok_value.varptr;
  2675. X            if (numvar && varptr2->type==VCHAR){
  2676. X                sprintf(Msg,"Error: %s and %s are of different types",
  2677. X                    varptr1->name, varptr2->name);
  2678. X                S_abort();
  2679. X            }
  2680. X            if (numvar)
  2681. X                testnum = varptr2->u.num;
  2682. X            else
  2683. X                cmpstr = varptr2->u.str;
  2684. X            break;
  2685. X        default:
  2686. X            S_abort();
  2687. X    }
  2688. X    if (numvar){
  2689. X        status = (varptr1->u.num==testnum);
  2690. X        if (compmode==EQ)
  2691. X            return status;
  2692. X        if (compmode==NEQ)
  2693. X            return !status;
  2694. X        status = (varptr1->u.num > testnum);
  2695. X        if (compmode==MORETHAN)
  2696. X            return status;
  2697. X        else
  2698. X            return !status;
  2699. X    }
  2700. X    status = strcmp(varptr1->u.str,cmpstr);
  2701. X    if (compmode==EQ)
  2702. X        return(status==0);
  2703. X    if (compmode==NEQ)
  2704. X        return status;
  2705. X    if (compmode==MORETHAN)
  2706. X        return(status>0);
  2707. X    else
  2708. X        return(status<0);
  2709. X}
  2710. X
  2711. static char *
  2712. S_construct(pcptr)
  2713. char **pcptr;
  2714. X{
  2715. X    char *cptr;
  2716. X    static char newstring[VMAXSIZE+10];
  2717. X    TOK_TYPE nexttype;
  2718. X
  2719. X    newstring[0] = '\0';
  2720. X    cptr = newstring;
  2721. X    while ((nexttype=lexan(pcptr))!=NULLTOK){
  2722. X        if (strlen(newstring)>VMAXSIZE){
  2723. X            newstring[VMAXSIZE] = '\0';
  2724. X            break;
  2725. X        }
  2726. X        switch (nexttype){
  2727. X            case TERMINAT:
  2728. X                break;
  2729. X            case NUMBER:
  2730. X                sprintf(cptr,"%ld",tok_value.numval);
  2731. X                cptr += strlen(cptr);
  2732. X                continue;
  2733. X            case LITERAL:
  2734. X                sprintf(cptr,"%s",S_qstrip(tok_value.strptr));
  2735. X                cptr += strlen(cptr);
  2736. X                continue;
  2737. X            case VARNAME:
  2738. X                if (tok_value.varptr->type != VCHAR)
  2739. X                    sprintf(cptr,"%ld",tok_value.varptr->u.num);
  2740. X                else
  2741. X                    sprintf(cptr,"%s",tok_value.varptr->u.str);
  2742. X                cptr += strlen(cptr);
  2743. X                continue;
  2744. X            default:
  2745. X                S_abort();
  2746. X        }
  2747. X        break;
  2748. X    }
  2749. X    return newstring;
  2750. X}
  2751. X
  2752. X/*        stack protection and break/continue stuff */
  2753. X
  2754. static int    nest_while,        /* number of nested loops */
  2755. X            nest_parse,        /* number of nested calls to S_parse() */
  2756. X            nest_cmd;        /* number of nested commands */
  2757. X
  2758. static long deadline;        /* deadline for 'timeout' keyword */
  2759. X
  2760. jmp_buf env;                /* cell for environment, setjmp */
  2761. X
  2762. X#define CMDNEST        8        /* max number of nested scripts */
  2763. X#define PARSNEST    50        /* max number of nested calls to parser */
  2764. X
  2765. static char *areas[CMDNEST];
  2766. X
  2767. X#define DNP        --nest_parse
  2768. X#define BCCHK(x) if(x==SBREAK||x==SCONTNUE){DNP;return(nest_while?x:S_abort());}
  2769. X
  2770. X/* cleanup any debris left after a non-trapped keyboard interrupt */
  2771. static void 
  2772. S_bombout()
  2773. X{
  2774. X    int i;
  2775. X
  2776. X    for (i=0; i<nest_cmd; ++i){
  2777. X        if (areas[i])
  2778. X            free(areas[i]),
  2779. X            areas[i] = NIL(char);
  2780. X    }
  2781. X    nest_cmd = nest_while = nest_parse = 0;
  2782. X    deadline = 0;
  2783. X}
  2784. X
  2785. X/*    S_parse() */
  2786. static char    *intercom,        /* last previous value of S_parse program ctr */
  2787. X            *tvector;        /* trap vector */
  2788. static FILE *savetfp;        /* to stash tfp when tfp redirected */
  2789. static 
  2790. S_parse(p_pc, t_invoke)
  2791. char *p_pc;
  2792. TOK_TYPE t_invoke;
  2793. X{
  2794. X    long n;                    /* "leading" number for primitives */
  2795. X    int i,
  2796. X        status,                /* status of last performed operation */
  2797. X        retval,                /* value to be returned by this function */
  2798. X        testing,            /* flag set if within 'if' or 'while' clause */
  2799. X        direction,            /* if 1, increment, if -1, decrement */
  2800. X        w_status,            /* used to correct nest_parse in 'while' */
  2801. X        counter,            /* for WHILE and IF, to track keywords */
  2802. X        negating;            /* ! operator in effect for comparisons */
  2803. X    char *cptr, *S_WHILE, *S_DO, *S_DONE;
  2804. X    TOK_TYPE nexttype;
  2805. X    VAR *varptr1;
  2806. X
  2807. X    ++nest_parse;
  2808. X    testing = (t_invoke==THEN || t_invoke==DO);
  2809. X    retval = status = negating = FALSE;
  2810. X    n = -1;
  2811. X    counter = 0;
  2812. X    while (TRUE){
  2813. X        /* we come through here only at the beginning of expressions */
  2814. X        if (deadline){
  2815. X            if (time(0)>deadline){        /* deadline; exit current script */
  2816. X                deadline = 0;
  2817. X                longjmp(env,TIMEOUT);
  2818. X            }
  2819. X        }
  2820. X        if (BREAK && tvector && t_invoke!=ENDTRAP)    /* interrupt trap */
  2821. X            BREAK = FALSE,
  2822. X            cptr = tvector,
  2823. X            status = S_parse(cptr,ENDTRAP),
  2824. X            DNP;
  2825. X
  2826. X        BREAK = FALSE;
  2827. X        retval = testing ? (retval || status) : status;
  2828. X        direction = 1;
  2829. X        intercom = p_pc;
  2830. X        nexttype = lexan(&p_pc);
  2831. X                /* check for list terminator */
  2832. X        if (nexttype==t_invoke || (t_invoke==ENDIF && nexttype==ELSE)){
  2833. X            if (debugflag && testing)
  2834. X                fprintf(tfp,"\r\n\t\t\t\t\t\t\tCondition: %s\r\n",
  2835. X                    retval ? "TRUE" : "FALSE");
  2836. X            return retval;
  2837. X        }
  2838. X        switch (nexttype){
  2839. X            case NULLTOK:        /* inconsistent list terminators */
  2840. X            case DO:                                /**/
  2841. X            case DONE:                                /**/
  2842. X            case THEN:                                /**/
  2843. X            case ELSE:                                /**/
  2844. X            case ENDIF:                                /**/
  2845. X            case TTERROR:                            /**/
  2846. X            case EFLAG:            /* not at beginning of expressions */
  2847. X            case EQ:                                /**/
  2848. X            case NEQ:                                /**/
  2849. X            case MORETHAN:                            /**/
  2850. X            case LESSTHAN:                            /**/
  2851. X                S_abort();
  2852. X            case LITERAL:
  2853. X                if (!testing)
  2854. X                    S_abort();
  2855. X                status = !!strlen(tok_value.strptr);
  2856. X                if (negating)
  2857. X                    status = !status;
  2858. X                continue;
  2859. X            case NEGATE:
  2860. X                if (!testing || negating)
  2861. X                    S_abort();
  2862. X                negating = 1;
  2863. X                continue;
  2864. X            case NUMBER:
  2865. X                if (n!=(-1))
  2866. X                    S_abort();
  2867. X                n = tok_value.numval;
  2868. X                continue;
  2869. X            case TERMINAT:
  2870. X                negating = 0;
  2871. X                continue;
  2872. X            case ACTION:
  2873. X                status = S_perform(&p_pc);
  2874. X                if (negating)
  2875. X                    status = !status;
  2876. X                break;
  2877. X            case AFFIRM:
  2878. X                status = S_affirm();
  2879. X                if (negating)
  2880. X                    status = !status;
  2881. X                break;
  2882. X            case SBREAK:
  2883. X            case SCONTNUE:
  2884. X                if (testing || t_invoke==NULLTOK)
  2885. X                    S_abort();
  2886. X                return(nexttype==SBREAK ? SBREAK : SCONTNUE);
  2887. X            case CALL:
  2888. X                lexan(&p_pc);
  2889. X                i = nest_while;
  2890. X                nest_while = 0;
  2891. X                S_call(S_qstrip(tok_value.strptr));
  2892. X                DNP;
  2893. X                nest_while = i;
  2894. X                break;
  2895. X            case COMMENT:
  2896. X                continue;
  2897. X            case DECR:
  2898. X                direction = (-1);        /* and fall through to... */
  2899. X            case INCR:
  2900. X                if ((nexttype=lexan(&p_pc))!=VARNAME)
  2901. X                    S_abort();
  2902. X                if ((tok_value.varptr->type) != VNUM){
  2903. X                    sprintf(Msg,"Error: %s is not a numeric variable",
  2904. X                        tok_value.varptr->name);
  2905. X                    S_abort();
  2906. X                }
  2907. X                status = S_addsub(direction);
  2908. X                if (negating) status =
  2909. X                    !status;
  2910. X                break;
  2911. X            case ECHOS:
  2912. X                status = 1;
  2913. X                if ((nexttype = lexan(&p_pc))==EFLAG)
  2914. X                    status = 0;
  2915. X                else
  2916. X                    p_pc = intercom,
  2917. X                    lexan(&p_pc);
  2918. X                cptr = S_construct(&p_pc);
  2919. X                fprintf(tfp,"%s",cptr);
  2920. X                if (status)
  2921. X                    if (tfp != cfp)
  2922. X                        fputc('\r',tfp),
  2923. X                    fputc('\n',tfp);
  2924. X                else
  2925. X                    status = 1;
  2926. X                break;
  2927. X            case EXIT:
  2928. X                longjmp(env,EXIT);
  2929. X            case READ:
  2930. X                status = S_read(&p_pc);
  2931. X                break;
  2932. X            case SET:
  2933. X                status = S_set(&p_pc);
  2934. X                if (negating)
  2935. X                    status = !status;
  2936. X                break;
  2937. X            case SHELL:
  2938. X                sprintf(word,"!");
  2939. X                sprintf(line,"%s",S_construct(&p_pc));
  2940. X                if (tfp==cfp)
  2941. X                    sprintf(&line[strlen(line)]," >>%s",captfile);
  2942. X                lptr = line;
  2943. X                status = !s_shell();
  2944. X                if (negating)
  2945. X                    status = !status;
  2946. X                break;
  2947. X            case PIPE:
  2948. X                sprintf(word,"$");
  2949. X                sprintf(line,"%s",S_construct(&p_pc));
  2950. X                lptr = line;
  2951. X                status = !s_shell();
  2952. X                if (negating)
  2953. X                    status = !status;
  2954. X                break;
  2955. X            case SFILE:
  2956. X                savetfp = tfp;
  2957. X                if (!capture){
  2958. X                    S2("Capture option not on");
  2959. X                    while ((nexttype=lexan(&p_pc))!=TERMINAT &&
  2960. X                        nexttype != NULLTOK);
  2961. X                    break;
  2962. X                }
  2963. X                tfp = cfp;
  2964. X                status = S_parse(p_pc,TERMINAT);
  2965. X                if (negating)
  2966. X                    status = !status;
  2967. X                DNP;
  2968. X                while ((nexttype=lexan(&p_pc)) !=TERMINAT &&
  2969. X                    nexttype != NULLTOK)
  2970. X                    ;
  2971. X                tfp = savetfp;
  2972. X                fseek(cfp,0L,2);
  2973. X                break;
  2974. X            case STRUE:
  2975. X            case SFALSE:
  2976. X                status = (nexttype==STRUE);
  2977. X                intercom = p_pc;
  2978. X                if ((nexttype=(lexan(&p_pc)))!=TERMINAT && nexttype!=NULLTOK)
  2979. X                        S_abort();
  2980. X                if (negating)
  2981. X                    status = !status;
  2982. X                break;
  2983. X            case STRAP:
  2984. X                tvector = p_pc;
  2985. X                cptr = tvector;
  2986. X                while ((nexttype=lexan(&p_pc))!=ENDTRAP)
  2987. X                    if (nexttype==NULLTOK)
  2988. X                        S_abort();
  2989. X                break;
  2990. X            case TIMEOUT:
  2991. X                if ((nexttype=lexan(&p_pc))!=NUMBER)
  2992. X                    S_abort();
  2993. X                if (tok_value.numval>=0){
  2994. X                    deadline = 0;
  2995. X                    if (tok_value.numval)
  2996. X                        deadline = time(0) + (tok_value.numval*60);
  2997. X                }
  2998. X                while ((nexttype=lexan(&p_pc))!=TERMINAT && nexttype!=NULLTOK)
  2999. X                    ;
  3000. X                break;
  3001. X            case UNSET:
  3002. X                if ((nexttype=lexan(&p_pc))!=VARNAME)
  3003. X                    S_abort();
  3004. X                status = 1;
  3005. X                unsetvar(tok_value.varptr->name);
  3006. X                break;
  3007. X            case UNTRAP:
  3008. X                tvector = NIL(char);
  3009. X                if ((nexttype=lexan(&p_pc))!=TERMINAT)
  3010. X                    S_abort();
  3011. X                break;
  3012. X            case XCSET:
  3013. X                i = 0;
  3014. X                line[0] = '\0';
  3015. X                while (*p_pc!='\n')
  3016. X                    line[i++] = *(p_pc++);
  3017. X                line[i] = '\0';
  3018. X                lptr = line;
  3019. X                s_set();
  3020. X                break;
  3021. X            case VARNAME:
  3022. X                varptr1 = tok_value.varptr;
  3023. X                if (!testing){
  3024. X                    if (varptr1->type==VCHAR || n!=(-1))
  3025. X                        S_abort();
  3026. X                    n = varptr1->u.num;
  3027. X                    continue;
  3028. X                }
  3029. X                status = S_varcmp(varptr1,&p_pc);
  3030. X                if (negating)
  3031. X                    status = !status;
  3032. X                break;
  3033. X            case IF:
  3034. X                if (nest_parse > PARSNEST){
  3035. X                    sprintf(Msg,"Nesting level too deep");
  3036. X                    S_abort();
  3037. X                }
  3038. X                status = S_parse(p_pc,THEN);
  3039. X                DNP;
  3040. X                if (status==TRUE){
  3041. X                    lexan(&intercom);
  3042. X                    p_pc = intercom;
  3043. X                    status = S_parse(p_pc,ENDIF);
  3044. X                    BCCHK(status)
  3045. X                    DNP;
  3046. X                    cptr = intercom;
  3047. X                    nexttype = lexan(&cptr);
  3048. X                    p_pc = cptr;
  3049. X                    if (nexttype==ELSE){
  3050. X                        counter = 0;
  3051. X                        while (TRUE){
  3052. X                            switch ((nexttype=lexan(&cptr))){
  3053. X                                case IF:
  3054. X                                    ++counter;
  3055. X                                    continue;
  3056. X                                case ENDIF:
  3057. X                                    if (counter){
  3058. X                                        --counter;
  3059. X                                        continue;
  3060. X                                    }
  3061. X                                    p_pc = cptr;
  3062. X                                    break;
  3063. X                                case NULLTOK:
  3064. X                                    S_abort();
  3065. X                                default:
  3066. X                                    continue;
  3067. X                            }
  3068. X                            break;
  3069. X                        } /* now p_pc points to just after matching 'endif' */
  3070. X                    }
  3071. X                }
  3072. X                else {    /* intercom is now the THEN token */
  3073. X                    counter = 0;
  3074. X                    cptr = intercom;
  3075. X                    while (TRUE){
  3076. X                        switch ((nexttype=lexan(&cptr))){
  3077. X                            case IF:
  3078. X                                ++counter;
  3079. X                                continue;
  3080. X                            case ELSE:
  3081. X                            case ENDIF:
  3082. X                                if (counter){
  3083. X                                    --counter;
  3084. X                                    continue;
  3085. X                                }
  3086. X                                p_pc = cptr;
  3087. X                                break;
  3088. X                            case NULLTOK:
  3089. X                                S_abort();
  3090. X                            default:
  3091. X                                continue;
  3092. X                        }
  3093. X                        break;
  3094. X                    }
  3095. X                    if (nexttype==ELSE){
  3096. X                        status = S_parse(p_pc,ENDIF);
  3097. X                        BCCHK(status)
  3098. X                        DNP;
  3099. X                        p_pc = intercom;
  3100. X                        lexan(&p_pc);
  3101. X                    }
  3102. X                    /* p_pc now points to just after ENDIF */
  3103. X                }
  3104. X                break;
  3105. X            case WHILE:
  3106. X                if (nest_parse > PARSNEST){
  3107. X                    sprintf(Msg,"Nesting level too deep");
  3108. X                    S_abort();
  3109. X                }
  3110. X                S_WHILE = p_pc;
  3111. X                S_DO = S_DONE = NIL(char);
  3112. X                while ((w_status=S_parse(S_WHILE,DO))==TRUE){
  3113. X                    DNP;
  3114. X                    --nest_while;
  3115. X                    if (!S_DO)
  3116. X                        lexan(&intercom),
  3117. X                        S_DO = intercom;
  3118. X                    status = S_parse(S_DO,DONE);
  3119. X                    DNP;
  3120. X                    --nest_while;
  3121. X                    if (status == SBREAK){
  3122. X                        status = 0;
  3123. X                        break;
  3124. X                    }                    /* note SCONTNUE is automatic */
  3125. X                    if (!S_DONE && status!=SCONTNUE)
  3126. X                        lexan(&intercom),
  3127. X                        S_DONE = intercom;
  3128. X                }
  3129. X                if (S_DONE)
  3130. X                    p_pc = S_DONE;
  3131. X                else {
  3132. X                    cptr = S_DO ? S_DO : S_WHILE;
  3133. X                    while (TRUE){
  3134. X                        switch ((nexttype=lexan(&cptr))){
  3135. X                            case WHILE:
  3136. X                                ++counter;
  3137. X                                continue;
  3138. X                            case DONE:
  3139. X                                if (counter){
  3140. X                                    --counter;
  3141. X                                    continue;
  3142. X                                }
  3143. X                                p_pc = cptr;
  3144. X                                break;
  3145. X                            case NULLTOK:
  3146. X                                S_abort();
  3147. X                            default:
  3148. X                                continue;
  3149. X                        }
  3150. X                        break;
  3151. X                    }
  3152. X                    /* p_pc now points to just after matching 'done' */
  3153. X                }
  3154. X                if (w_status==FALSE)
  3155. X                    DNP;
  3156. X                break;
  3157. X        } /* end of main switch, whew */
  3158. X        if (t_invoke==TERMINAT)
  3159. X            return status;
  3160. X        n = -1;
  3161. X    }
  3162. X}
  3163. X
  3164. X/* load a script and call S_parse to run it */
  3165. static void 
  3166. S_call(scriptname)
  3167. char *scriptname;
  3168. X{
  3169. X    int i;
  3170. X    jmp_buf senv;
  3171. X    char *oldtvec, *newptr, *oldptr, script[SM_BUFF];
  3172. X    long filesize;
  3173. X    FILE *scriptfp;
  3174. X    static struct stat statbuf;
  3175. X
  3176. X    strcpy(script, scriptname);
  3177. X    memset(Msg, 0, SM_BUFF);
  3178. X    if (++nest_cmd>CMDNEST){
  3179. X        S2("Too many nested scripts");
  3180. X        --nest_cmd;
  3181. X        return;
  3182. X    }
  3183. X    if (!(scriptfp = openfile(script))){
  3184. X        sprintf(Msg,"Can't open '%s'",script);
  3185. X        S2(Msg);
  3186. X        --nest_cmd;
  3187. X        return;
  3188. X    }
  3189. X
  3190. X    /* this succeeds, openfile() has called isregfile() to stat the file */
  3191. X    fstat(fileno(scriptfp),&statbuf);
  3192. X    filesize = statbuf.st_size;
  3193. X
  3194. X    areas[nest_cmd - 1] = NIL(char);
  3195. X    if (!(areas[nest_cmd-1]=(char*)calloc((unsigned)filesize+10,1))){
  3196. X        sprintf(Msg,"%s: allocation error",script);
  3197. X        S2(Msg);
  3198. X        fclose(scriptfp);
  3199. X        --nest_cmd;
  3200. X        return;
  3201. X    }
  3202. X    if (strcmp(STARTUP,script))
  3203. X        sprintf(Msg,"Running %s",script),
  3204. X        S2(Msg);
  3205. X    *(areas[nest_cmd - 1]) = '\n';
  3206. X    fread((areas[nest_cmd - 1] + 1),filesize,1,scriptfp);
  3207. X    *(areas[nest_cmd - 1] + filesize + 1) = '\0';
  3208. X    fclose(scriptfp);
  3209. X
  3210. X    oldtvec = tvector;
  3211. X    tvector = NIL(char);
  3212. X    newptr = (char *)senv;
  3213. X    oldptr = (char *)env;
  3214. X    for (i=0; i<sizeof(env); ++i)
  3215. X        *(newptr++) = *(oldptr++);
  3216. X    if ((i=setjmp(env))==0)
  3217. X        S_parse(areas[nest_cmd - 1],NULLTOK);
  3218. X    else if (i==TTERROR)
  3219. X        S2("Abnormal script termination");
  3220. X    else if (i==TIMEOUT)
  3221. X        S2("Timeout");
  3222. X    else if (i==EXIT)
  3223. X        S2("Script encountered 'exit'");
  3224. X
  3225. X    tvector = oldtvec;
  3226. X    newptr = (char *)env;
  3227. X    oldptr = (char *)senv;
  3228. X    for (i=0; i<sizeof(env); ++i)
  3229. X        *(newptr++) = *(oldptr++);
  3230. X    --nest_cmd;
  3231. X    if (areas[nest_cmd])
  3232. X        free(areas[nest_cmd]);
  3233. X
  3234. X    if (strcmp(STARTUP,script))
  3235. X        sprintf(Msg,"%s COMPLETE",script),
  3236. X        S2(Msg);
  3237. X}
  3238. X
  3239. static 
  3240. S_abort()
  3241. X{
  3242. X    char *cptr;
  3243. X
  3244. X    if (*Msg)
  3245. X        S2(Msg);
  3246. X
  3247. X    cptr = intercom;
  3248. X    while (*cptr && *cptr!='\n')
  3249. X        --cptr;
  3250. X
  3251. X    ++cptr;
  3252. X    while (*cptr && *cptr!='\n')
  3253. X        fputc(*(cptr++),tfp);
  3254. X    fputc('\r',tfp),
  3255. X    fputc('\n',tfp);
  3256. X
  3257. X    if (tfp==cfp)
  3258. X        tfp = savetfp;
  3259. X    unsetall();
  3260. X    longjmp(env,TTERROR);
  3261. X}
  3262. X
  3263. X/*    get_bound_char() get a character from the user's keyboard.  If the terminal
  3264. X    mode escape character is seen, look ahead to the next character and act on
  3265. X    it. If unrecognized, simply swallow the escape character and return the
  3266. X    second character. Returns ASCII character code, or XCBIND function code
  3267. X*/
  3268. get_bound_char()
  3269. X{
  3270. X    int            c, lc;
  3271. X    binding_t    *ptr;
  3272. X    static char    *emit_string = NIL(char);
  3273. X
  3274. X    if (emit_string) {
  3275. X        c = *(emit_string++);
  3276. X        if (!c)
  3277. X            emit_string = NIL(char);
  3278. X    }
  3279. X
  3280. X    if (!emit_string) {
  3281. X        c = getchar();
  3282. X        if (c == my_escape) {
  3283. X            lc = tolower(c=getchar());
  3284. X            for (ptr = first_binding; ptr; ptr = ptr->bs_next){
  3285. X                if (ptr->bs_c == lc){
  3286. X                    switch (ptr->bs_function){
  3287. X                    case EMITSTR:
  3288. X                        emit_string = ptr->bs_string;
  3289. X                        return *(emit_string++);
  3290. X                    case DOSCRPT:
  3291. X                        strcpy(ddsname, ptr->bs_string);
  3292. X                        return DOSCRPT;
  3293. X                    default:
  3294. X                        return ptr->bs_function;
  3295. X                    }
  3296. X                }
  3297. X            }
  3298. X        }
  3299. X    }
  3300. X    return c;
  3301. X}
  3302. X
  3303. X/*    default_bindings restores XC to its default key bindings.
  3304. X    Uppercase keys are mapped to lower case.
  3305. X*/
  3306. void 
  3307. default_bindings()
  3308. X{
  3309. X    bind_key('/', HLPCHAR, "");
  3310. X    bind_key('?', HLPCHAR, "");
  3311. X    bind_key('b', BRKCHAR, "");
  3312. X    bind_key('d', DIALCHR, "");
  3313. X    bind_key('f', DIVCHAR, "");
  3314. X    bind_key('h', HUPCHAR, "");
  3315. X    bind_key('n', CAPTEND, "");
  3316. X    bind_key('q', QUITCHR, "");
  3317. X    bind_key('s', SCRPCHR, "");
  3318. X    bind_key('x', ENDCHAR, "");
  3319. X    bind_key('y', CAPTYES, "");
  3320. X}
  3321. X
  3322. X/*    show_emit() performs an unctrl() operation on an entire buffer. */
  3323. static void 
  3324. show_emit(str)
  3325. char *str;
  3326. X{
  3327. X    while (*str)
  3328. X        fprintf(tfp,"%s", unctrl(*str++));
  3329. X}
  3330. X
  3331. X/*    get_function() returns the description corresponding to the function code
  3332. X    specified by (code).
  3333. X*/
  3334. static char *
  3335. get_function(code)
  3336. int code;
  3337. X{
  3338. X    bindstr_t *ptr = function_list;
  3339. X    int i;
  3340. X
  3341. X    for (i = 0; i < FUNCTION_COUNT; i++, ptr++)
  3342. X        if ((int) ptr->bf_function == code)
  3343. X            return ptr->bf_string;
  3344. X
  3345. X    return "???";
  3346. X}
  3347. X
  3348. show_bindings()
  3349. X{
  3350. X    binding_t *ptr = first_binding;
  3351. X    char *escape_str = strdup(unctrl(my_escape));
  3352. X    int curline = 0;
  3353. X
  3354. X    cls();
  3355. X
  3356. X    fprintf(tfp,"\tTERMINAL mode escape character is %s.\r\n\r\n", escape_str);
  3357. X    curline += 2;
  3358. X
  3359. X    while (ptr){
  3360. X        if (curline >= LI - 2){
  3361. X            S0("PRESS ENTER");
  3362. X            getline();
  3363. X            cls();
  3364. X            curline = 0;
  3365. X        }
  3366. X
  3367. X        fprintf(tfp,"\t%s - %-3.3s %s", escape_str, unctrl(ptr->bs_c),
  3368. X            get_function(ptr->bs_function));
  3369. X
  3370. X        switch (ptr->bs_function){
  3371. X        case EMITSTR:
  3372. X        case DOSCRPT:
  3373. X            fputc(' ',tfp);
  3374. X            fputc('"',tfp);
  3375. X            show_emit(ptr->bs_string);
  3376. X            fputc('"',tfp);
  3377. X            break;
  3378. X        }
  3379. X        fputc('\r',tfp);
  3380. X        fputc('\n',tfp);
  3381. X        curline++;
  3382. X
  3383. X        ptr = ptr->bs_next;
  3384. X    }
  3385. X
  3386. X    free(escape_str);
  3387. X}
  3388. X
  3389. X/*    find_function() returns the function code referenced by the name
  3390. X    pointed to by (name).  Returns BADFUNC if the name is unrecognized.
  3391. X*/
  3392. static 
  3393. bindfunc_t find_function(name)
  3394. char *name;
  3395. X{
  3396. X    bindstr_t *ptr = function_list;
  3397. X    int i;
  3398. X
  3399. X    for (i = 0; i < FUNCTION_COUNT; i++, ptr++)
  3400. X        if (strcmp(ptr->bf_name, name) == 0)
  3401. X            return ptr->bf_function;
  3402. X
  3403. X    return BADFUNC;
  3404. X}
  3405. END_OF_FILE
  3406. if test 44712 -ne `wc -c <'xcscrpt.c'`; then
  3407.     echo shar: \"'xcscrpt.c'\" unpacked with wrong size!
  3408. fi
  3409. # end of 'xcscrpt.c'
  3410. fi
  3411. echo shar: End of archive 3 \(of 3\).
  3412. cp /dev/null ark3isdone
  3413. MISSING=""
  3414. for I in 1 2 3 ; do
  3415.     if test ! -f ark${I}isdone ; then
  3416.     MISSING="${MISSING} ${I}"
  3417.     fi
  3418. done
  3419. if test "${MISSING}" = "" ; then
  3420.     echo You have unpacked all 3 archives.
  3421.     rm -f ark[1-9]isdone
  3422. else
  3423.     echo You still need to unpack the following archives:
  3424.     echo "        " ${MISSING}
  3425. fi
  3426. ##  End of shell archive.
  3427. exit 0
  3428.